how to set proxy_protocol based on SNI in stream block in Nginx?

Effective Routing and Client IP Preservation in Nginx Stream Module Based on SNI

In modern network architectures, it’s common to route TCP trafficโ€”such as HTTPSโ€”based on the Server Name Indication (SNI) provided during the TLS handshake. Nginx’s stream module offers powerful capabilities to handle such scenarios, but it also presents certain limitations, especially when it comes to conditionally enabling features like proxy_protocol based on dynamic variables.

This article discusses how to route TCP traffic based on SNI in Nginx, ensuring that client IP addresses are accurately logged, particularly when forwarding traffic to a local server with proxy_protocol enabled only for certain conditions.


The Challenge

Suppose you want to:

  • Route incoming SNI-based HTTPS traffic to different upstreams.
  • Forward incorrect or unexpected SNI traffic to a local server at port 4443.
  • Preserve the original client IP address when forwarding traffic to this local server, which requires enabling proxy_protocol.
  • Enable proxy_protocol only for traffic with incorrect SNI, without affecting traffic with the correct SNI.

The main complication is that in Nginx’s stream context, proxy_protocol cannot be dynamically set using variables โ€” it is a static directive that must be enabled at configuration load time.

The Existing Approach

Here’s an outline of the current configuration attempt:

“`nginx
stream {
# Map SNI to backend destinations
map $ssl_preread_server_name $backend {
“” local_website;
correct_website.com upstream_website;
default local_website;
}

# Determine if proxy_protocol should be enabled
map $ssl_preread_server_name $is_proxy_on {
    ""                      on;
    correct_website.com     off;
    default                 on;
}

upstream upstream_website {
    server upstream_website.com;
}

upstream local_website {
    server 127.0.0.1:4443;
}

server {
    listen 443;
    ssl_preread on;

    proxy_protocol $is_proxy_on;   # Attempt to conditionally enable proxy_protocol
    proxy_pass $backend;
}

}
“`

The core issue here: proxy_protocol cannot be controlled via variables. It requires a static proxy_protocol directive at configuration parse time, not dynamically per request.


Potential Solutions

While Apache and some


Leave a Reply

Your email address will not be published. Required fields are marked *