Open-Source Wikis

/

Traefik

/

Systems

/

Routing

traefik/traefik

Routing

Routing is the path from "request landed on an entry point" to "this is the middleware chain and service to use". It comes in three layers: the rule grammar, the muxers that compile rules into matchers, and the router factory that assembles trees from configuration.

The rule grammar

Traefik's rule language lives in pkg/rules/parser.go. Examples:

Host(`api.example.com`)
Host(`api.example.com`) && PathPrefix(`/v1`)
HostRegexp(`{subdomain:[a-z]+}.example.com`)
ClientIP(`10.0.0.0/8`) && Method(`GET`, `POST`)

The parser is a small handwritten recursive-descent implementation. It produces an AST of named matchers (Host, Header, HeaderRegexp, Method, Path, PathPrefix, PathRegexp, Query, QueryRegexp, ClientIP, HostRegexp, …) combined with &&, ||, and !.

For TCP, the grammar is similar but limited to matchers that work on the TLS handshake or address (HostSNI, HostSNIRegexp, ClientIP, ALPN).

HTTP muxer

pkg/muxer/http compiles rule ASTs into a matcher function func(*http.Request) bool and stores them in a routing tree.

Highlights:

  • Each matcher type has its own implementation file: host.go, headers.go, path.go, etc. (see the directory).
  • Routers are sorted by priority (default = rule length) so the most specific rule wins ties.
  • The muxer holds a *atomic.Pointer to its handler tree. pkg/server/router/router.go swaps the pointer after each configuration apply.

The HTTP muxer is the only matcher consulted on the hot path for HTTP traffic. There is no global regex evaluation — rules are split into typed matchers.

TCP muxer

pkg/muxer/tcp operates on the bytes the kernel delivered. Its main job is:

  • Parse the TLS ClientHello (when present) to extract SNI and ALPN.
  • Match TCP routers against HostSNI, ClientIP, and ALPN.
  • Select between TLS termination, TLS passthrough, or raw TCP.

Because TLS sniffing must complete before the routing decision, the entry-point implementation in pkg/server/server_entrypoint_tcp.go peeks the first bytes via a buffered conn before handing off to the muxer.

Router factory

pkg/server/routerfactory.go is the listener for configuration changes. Its public API:

type RouterFactory struct {
    // ...
}

func (f *RouterFactory) CreateRouters(confDyn *runtime.Configuration) (
    map[string]http.Handler,
    map[string]*tcprouter.Router,
    map[string]udprouter.Handler,
)

For each configured entry point it returns the freshly built handler tree. The watcher feeds the result back into the entry points via SwitchRouter.

The HTTP build pipeline is implemented in pkg/server/router/router.go:

graph TD
    Conf[runtime.Configuration] --> RouterMgr[manager<br/>pkg/server/router/router.go]
    RouterMgr -->|for each router| BuildChain
    BuildChain --> Mids[middleware chain<br/>pkg/server/middleware/middlewares.go]
    BuildChain --> Svc[service handler<br/>pkg/server/service/service.go]
    Mids --> ObsWrap[observability wrapper<br/>pkg/server/middleware/observability.go]
    Svc --> LB[load balancer<br/>pkg/server/service/loadbalancer/*]
    BuildChain --> Mux[register on muxer<br/>pkg/muxer/http]

Each step records a Status/Err on the runtime model. If a middleware reference is unresolved or a TLS option is unknown, the router gets disabled status with the reason — the dashboard shows it under "Errors".

Recursion and denial

pkg/server/router/deny.go and pkg/middlewares/denyrouterrecursion prevent middleware/router cycles. A router referenced by another router that ultimately points back to itself is rejected with a "router recursion detected" error.

TCP and UDP routers

pkg/server/router/tcp and pkg/server/router/udp are smaller siblings of the HTTP router. They consume runtime.TCPConfiguration and runtime.UDPConfiguration and register entries on the corresponding muxers. TCP routers can carry middlewares too — see pkg/middlewares/tcp/.

Internal handlers

A few routes are not user-defined. The pkg/server/service/internalhandler.go wires up:

  • api@internal — the dashboard / API.
  • dashboard@internal — the static-asset handler for the dashboard.
  • ping@internal/ping.
  • prometheus@internal/metrics.
  • acme-http@internal — HTTP-01 challenge handler.
  • noop@internal — a no-op service used as a placeholder.

These internal services have stable names so that other configurations can reference them. The Tailscale and Traefik providers (pkg/provider/tailscale, pkg/provider/traefik) inject routers that point at these internal services.

How a single request resolves

sequenceDiagram
    participant C as Client
    participant EP as TCP entry point
    participant M as HTTP muxer
    participant R as Router
    participant Mid as Middleware chain
    participant S as Service handler
    participant LB as Load balancer
    participant B as Backend

    C->>EP: TCP/TLS handshake
    EP->>EP: TLS termination via tlsmanager
    EP->>M: HTTP request
    M->>M: evaluate rules in priority order
    M->>R: matched router
    R->>Mid: ServeHTTP(...)
    Mid->>Mid: each middleware decides to pass through or short-circuit
    Mid->>S: ServeHTTP(...)
    S->>LB: pick a backend
    LB->>B: forward request
    B-->>LB: response
    LB-->>S: response
    S-->>Mid: response (modifiers run on the way back)
    Mid-->>R: response
    R-->>M: response
    M-->>EP: response
    EP-->>C: response (with logging, metrics, tracing all captured)

Key files

File Purpose
pkg/rules/parser.go Rule grammar parser.
pkg/muxer/http/muxer.go HTTP muxer + matcher tree.
pkg/muxer/http/host.go, path.go, headers.go, … Per-matcher implementations.
pkg/muxer/tcp/muxer.go TCP muxer using SNI/ALPN.
pkg/server/router/router.go HTTP router factory.
pkg/server/router/deny.go Recursion guard.
pkg/server/router/tcp/router.go TCP router factory.
pkg/server/router/udp/router.go UDP router factory.
pkg/server/routerfactory.go Listener wiring + entry-point dispatch.
pkg/server/service/internalhandler.go Internal service registry.

Entry points for modification

  • Adding a new matcher: implement it under pkg/muxer/http/ (or pkg/muxer/tcp/), register it in the parser's matcher table in pkg/rules/parser.go.
  • Adding a new internal service: register it in pkg/server/service/internalhandler.go.
  • Changing how routers are sorted: see the priority logic in pkg/server/router/router.go.

For the middlewares attached to a router, see Middlewares. For the services those routers point at, see Service and load balancing.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Routing – Traefik wiki | Factory