Routing Rules
Routing Rules
SNI Router supports three types of routing rules, each configured via environment variables.
TLS / SNI routing
TLS routing rules match on the SNI hostname in the TLS ClientHello and forward the raw TCP stream to the matching backend. No decryption occurs.
Numbered rules — SNI_ROUTE_N
The simplest form: one variable per rule, numbered consecutively from 1.
environment:
SNI_ROUTE_1: "app1.example.com:192.168.1.10:443"
SNI_ROUTE_2: "app2.example.com:192.168.1.20:443"
SNI_ROUTE_3: "api.example.com:192.168.1.30:443"
SNI_DEFAULT: "192.168.1.10:443"
Format: hostname:backend_ip:backend_port
Multiline rules — SNI_ROUTES
For deployments with many routes, the block-scalar YAML syntax is more readable:
environment:
SNI_ROUTES: |
app1.example.com:192.168.1.10:443
app2.example.com:192.168.1.20:443
# This is a comment — ignored
api.example.com:192.168.1.30:443
SNI_DEFAULT: "192.168.1.10:443"
SNI_ROUTE_N and SNI_ROUTES can be combined in the same deployment. Numbered rules are processed first.
Wildcard rules
Prefix a hostname with *. to match any subdomain:
SNI_ROUTES: |
app1.example.com:192.168.1.10:443
*.staging.example.com:192.168.1.30:443
The wildcard *.staging.example.com matches deploy.staging.example.com, test.staging.example.com, etc.
Automatic priority sorting
HAProxy uses first-match semantics. SNI Router automatically sorts rules so that exact hostnames always take precedence over wildcards, regardless of declaration order.
# All three layouts produce identical routing behaviour.
# Declaration order does not matter.
# Layout A — exact first
SNI_ROUTES: |
docs.example.com:192.168.1.20:443
*.example.com:192.168.1.10:443
# Layout B — wildcard first (same result)
SNI_ROUTES: |
*.example.com:192.168.1.10:443
docs.example.com:192.168.1.20:443
# Layout C — mixed with numbered rules
SNI_ROUTE_1: "docs.example.com:192.168.1.20:443"
SNI_ROUTES: |
*.example.com:192.168.1.10:443
In all three cases, a connection to docs.example.com reaches 192.168.1.20 and any other subdomain reaches 192.168.1.10.
Default backend
SNI_DEFAULT is required and receives all connections that do not match any rule:
SNI_DEFAULT: "192.168.1.10:443"
Plain TCP routing
TCP routing forwards connections arriving on a specific listen port to a backend — no TLS or SNI inspection.
environment:
# PostgreSQL on port 5432 → internal host
TCP_ROUTE_1: "5432:192.168.1.40:5432"
# MQTT on port 1883 → another internal host
TCP_ROUTE_2: "1883:192.168.1.50:1883"
Format: listen_port:backend_ip:backend_port
TCP_ROUTE_N port must be exposed in the ports: section of docker-compose.yml:ports:
- "5432:5432"
- "1883:1883"
HTTP routing
The HTTP frontend forwards plain HTTP requests to specific backends. It is useful for routing Let's Encrypt http-01 ACME challenges and redirecting all other HTTP traffic to HTTPS.
The HTTP frontend is activated when SNI_HTTP_REDIRECT=true or when any HTTP_ROUTE_N / HTTP_ROUTES variable is set.
environment:
SNI_HTTP_REDIRECT: "true"
SNI_HTTP_PORT: "80" # default is 80
# Forward http-01 challenges for these hosts to the correct Traefik instance.
# Everything else is 301-redirected to HTTPS.
HTTP_ROUTES: |
app1.example.com:192.168.1.10:80
app2.example.com:192.168.1.20:80
Format: hostname:backend_ip:backend_port
HTTP_ROUTE_N and HTTP_ROUTES follow the same conventions as their SNI counterparts, including wildcard matching and priority sorting.
80 (or SNI_HTTP_PORT) must be exposed in the ports: section of docker-compose.yml.