Skip to main content

Routers

Connecting Requests to Services Routers analyze incoming requests and determine which service should handle them based on configurable rules. They are part of the dynamic configuration.

HTTP Routers

Basic Router Configuration

http:
  routers:
    my-router:
      rule: "Host(`example.com`) && Path(`/api`)"
      service: api-service
      entryPoints:
        - websecure
      tls:
        certResolver: letsencrypt
rule
string
required
Matching rule that determines if this router handles the request
service
string
required
Name of the service to forward matching requests to
entryPoints
array
List of EntryPoints to listen on. If not specified, listens on all default EntryPoints

Routing Rules

Rules determine which requests match a router using matchers and logical operators.

Available Matchers

Host(`domain`)
matcher
Match requests to specific domain
rule: "Host(`example.com`)"
HostRegexp(`regexp`)
matcher
Match requests using regular expression
rule: "HostRegexp(`^.+\\.example\\.com$`)"
Path(`path`)
matcher
Match exact path
rule: "Path(`/api/users`)"
PathPrefix(`prefix`)
matcher
Match path prefix
rule: "PathPrefix(`/api`)"
PathRegexp(`regexp`)
matcher
Match path using regular expression
rule: "PathRegexp(`^/api/v[0-9]+`)"
Method(`method`)
matcher
Match HTTP method
rule: "Method(`POST`)"
Header(`key`, `value`)
matcher
Match requests with specific header
rule: "Header(`Content-Type`, `application/json`)"
HeaderRegexp(`key`, `regexp`)
matcher
Match header using regular expression
rule: "HeaderRegexp(`Content-Type`, `^application/(json|yaml)$`)"
Query(`key`, `value`)
matcher
Match query parameter
rule: "Query(`version`, `v2`)"
ClientIP(`ip`)
matcher
Match client IP address or CIDR range
rule: "ClientIP(`192.168.1.0/24`)"

Combining Rules

Use logical operators to create complex rules:
rule: "Host(`example.com`) && Path(`/api`)"

Rule Examples

http:
  routers:
    # Route by domain
    blog:
      rule: "Host(`blog.example.com`)"
      service: blog-service
    
    # Route subdomains with regex
    api:
      rule: "HostRegexp(`^api-[a-z]+\\.example\\.com$`)"
      service: api-service
    
    # Route multiple domains
    main:
      rule: "Host(`example.com`) || Host(`www.example.com`)"
      service: main-service

Priority

Routers are evaluated by priority. Higher priority routers are checked first.
http:
  routers:
    specific:
      rule: "Host(`api.example.com`) && Path(`/users/123`)"
      service: specific-service
      priority: 100
    
    general:
      rule: "Host(`api.example.com`)"
      service: general-service
      priority: 1
By default, priority equals the length of the rule. Longer rules have higher priority.

Middlewares

Attach middlewares to process requests before forwarding to services:
http:
  routers:
    secured-api:
      rule: "Host(`api.example.com`)"
      service: api-service
      middlewares:
        - auth
        - rate-limit
        - compress
  
  middlewares:
    auth:
      basicAuth:
        users:
          - "admin:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/"
    
    rate-limit:
      rateLimit:
        average: 100
        burst: 50
    
    compress:
      compress: {}
Middlewares are applied in the order they appear in the list.

TLS Configuration

Basic TLS

Enable TLS termination:
http:
  routers:
    secure:
      rule: "Host(`example.com`)"
      service: web-service
      entryPoints:
        - websecure
      tls: {}

Automatic Certificate Resolution

http:
  routers:
    secure:
      rule: "Host(`example.com`)"
      service: web-service
      tls:
        certResolver: letsencrypt
        domains:
          - main: "example.com"
            sans:
              - "*.example.com"
Wildcard certificates require DNS-01 challenge. HTTP-01 challenge only works for single domains.

TCP Routers

TCP routers handle non-HTTP TCP connections:
tcp:
  routers:
    postgres:
      rule: "HostSNI(`db.example.com`)"
      service: postgres-service
      entryPoints:
        - postgres
      tls:
        certResolver: letsencrypt

TCP Router Rules

HostSNI(`domain`)
matcher
Match Server Name Indication
rule: "HostSNI(`example.com`)"
HostSNI(`*`)
matcher
Match all non-TLS connections
rule: "HostSNI(`*`)"
ClientIP(`ip`)
matcher
Match client IP
rule: "ClientIP(`192.168.1.0/24`)"

TLS Passthrough

Forward TLS connections without decryption:
tcp:
  routers:
    secure-backend:
      rule: "HostSNI(`backend.example.com`)"
      service: backend-service
      tls:
        passthrough: true

Complete Example

http:
  routers:
    # Main website
    web:
      rule: "Host(`example.com`) || Host(`www.example.com`)"
      service: web-service
      entryPoints:
        - websecure
      middlewares:
        - redirect-www
        - compress
      tls:
        certResolver: letsencrypt
    
    # API with authentication
    api:
      rule: "Host(`api.example.com`) && PathPrefix(`/v2`)"
      service: api-service
      priority: 10
      middlewares:
        - api-auth
        - rate-limit
      tls:
        certResolver: letsencrypt
    
    # Admin panel (IP restricted)
    admin:
      rule: "Host(`admin.example.com`) && ClientIP(`192.168.1.0/24`)"
      service: admin-service
      middlewares:
        - admin-auth
      tls:
        certResolver: letsencrypt
  
  middlewares:
    redirect-www:
      redirectRegex:
        regex: "^https://example\\.com/(.*)"
        replacement: "https://www.example.com/${1}"
    
    api-auth:
      basicAuth:
        users:
          - "api:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/"
    
    admin-auth:
      basicAuth:
        users:
          - "admin:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/"
    
    rate-limit:
      rateLimit:
        average: 100
        burst: 50
    
    compress:
      compress: {}

Build docs developers (and LLMs) love