Configuration

Routing Rules

How to define TLS/SNI, TCP, and HTTP routing rules in SNI Router.

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

Each 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.

Port 80 (or SNI_HTTP_PORT) must be exposed in the ports: section of docker-compose.yml.
Copyright © 2026