Last active
September 17, 2025 12:07
-
-
Save mbentley/0e887c2af7863a562146ee23b121fb33 to your computer and use it in GitHub Desktop.
HAProxy SNI Example
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| global | |
| log /dev/log local0 | |
| log /dev/log local1 notice | |
| defaults | |
| log global | |
| mode tcp | |
| option tcplog | |
| option dontlognull | |
| timeout connect 5s | |
| timeout client 50s | |
| timeout client-fin 50s | |
| timeout server 50s | |
| timeout tunnel 1h | |
| default-server inter 15s fastinter 2s downinter 5s rise 3 fall 3 | |
| ### standalone stats page | |
| listen stats | |
| # accessible at http://192.168.1.100/haproxy?stats | |
| bind 0.0.0.0:8181 | |
| mode http | |
| option httplog | |
| stats enable | |
| stats admin if TRUE | |
| stats refresh 5m | |
| ### frontend servers | |
| frontend http | |
| bind 0.0.0.0:80 | |
| mode http | |
| # redirects from http to https | |
| redirect scheme https code 301 if { hdr(Host) -i site1.example.com } !{ ssl_fc } | |
| redirect scheme https code 301 if { hdr(Host) -i site1alias.example.com } !{ ssl_fc } | |
| redirect scheme https code 301 if { hdr(Host) -i site2.example.com } !{ ssl_fc } | |
| redirect scheme https code 301 if { hdr(Host) -i site3.example.com } !{ ssl_fc } | |
| redirect scheme https code 301 if { hdr(Host) -i site4.example.com } !{ ssl_fc } | |
| # set default backend | |
| default_backend default_http | |
| frontend https | |
| option tcplog | |
| bind 0.0.0.0:443 | |
| tcp-request inspect-delay 5s | |
| tcp-request content accept if { req_ssl_hello_type 1 } | |
| ## exact matches | |
| use_backend site1_https if { req.ssl_sni -i site1.example.com or site1alias.example.com } | |
| use_backend site2_https if { req.ssl_sni -i site2.example.com } | |
| use_backend site3_https if { req.ssl_sni -i site3.example.com } | |
| use_backend site4_https if { req.ssl_sni -i site4.example.com } | |
| # set default backend | |
| default_backend default_https | |
| ### backend servers | |
| backend default_http | |
| mode http | |
| server nginx_lb_http.sock unix@/run/nginx_lb_http.sock send-proxy weight 100 check | |
| backend default_https | |
| mode tcp | |
| server nginx_lb_https.sock unix@/run/nginx_lb_https.sock send-proxy weight 100 check | |
| backend site1_https | |
| mode tcp | |
| option ssl-hello-chk | |
| server server1:443 192.168.1.101:443 weight 100 check | |
| backend site2_https | |
| mode tcp | |
| option ssl-hello-chk | |
| server server2:443 192.168.1.102:443 weight 100 check | |
| backend site3_https | |
| mode tcp | |
| option ssl-hello-chk | |
| server server3:443 192.168.1.103:443 weight 100 check | |
| backend site4_https | |
| mode tcp | |
| option ssl-hello-chk | |
| server server4:443 192.168.1.104:443 weight 100 check |
What about ssl certificates ? How do i handle them, i am having problems with those
No idea if this actually works because I do not terminate SSL at the haproxy layer for anything but here's an example I generated:
global
log /dev/log local0
log /dev/log local1 notice
# SSL/TLS configuration for termination
ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384
ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets
ssl-default-server-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384
ssl-default-server-options ssl-min-ver TLSv1.2 no-tls-tickets
# Tune SSL performance
tune.ssl.default-dh-param 2048
defaults
log global
mode tcp
option tcplog
option dontlognull
timeout connect 5s
timeout client 50s
timeout client-fin 50s
timeout server 50s
timeout tunnel 1h
default-server inter 15s fastinter 2s downinter 5s rise 3 fall 3
### standalone stats page
listen stats
# accessible at http://192.168.1.100:8181/haproxy?stats
bind 0.0.0.0:8181
mode http
option httplog
stats enable
stats admin if TRUE
stats refresh 5m
### frontend servers - HTTP (redirects only)
frontend http
bind 0.0.0.0:80
mode http
option httplog
# redirects from http to https
redirect scheme https code 301 if { hdr(Host) -i site1.example.com } !{ ssl_fc }
redirect scheme https code 301 if { hdr(Host) -i site1alias.example.com } !{ ssl_fc }
redirect scheme https code 301 if { hdr(Host) -i site2.example.com } !{ ssl_fc }
redirect scheme https code 301 if { hdr(Host) -i site3.example.com } !{ ssl_fc }
redirect scheme https code 301 if { hdr(Host) -i site4.example.com } !{ ssl_fc }
redirect scheme https code 301 if { hdr(Host) -i site5.example.com } !{ ssl_fc }
# set default backend for non-matching hosts
default_backend default_http
### frontend servers - HTTPS with SNI passthrough (your original approach)
frontend https_passthrough
bind 0.0.0.0:443
mode tcp
option tcplog
tcp-request inspect-delay 5s
tcp-request content accept if { req_ssl_hello_type 1 }
## SNI-based routing - SSL passes through to backend servers
use_backend site1_https_passthrough if { req.ssl_sni -i site1.example.com }
use_backend site1_https_passthrough if { req.ssl_sni -i site1alias.example.com }
use_backend site2_https_passthrough if { req.ssl_sni -i site2.example.com }
use_backend site3_https_passthrough if { req.ssl_sni -i site3.example.com }
use_backend site4_https_passthrough if { req.ssl_sni -i site4.example.com }
# set default backend
default_backend default_https_passthrough
### frontend servers - HTTPS with SSL termination at HAProxy
frontend https_terminated
bind 0.0.0.0:8443 ssl crt /etc/haproxy/certs/site5.example.com.pem crt /etc/haproxy/certs/
mode http
option httplog
option forwardfor
# Add security headers
http-response set-header Strict-Transport-Security "max-age=31536000; includeSubDomains"
http-response set-header X-Frame-Options "SAMEORIGIN"
http-response set-header X-Content-Type-Options "nosniff"
# Route based on Host header (after SSL termination)
use_backend site5_http_backend if { hdr(Host) -i site5.example.com }
# set default backend
default_backend default_http_backend
### backend servers - HTTP backends
backend default_http
mode http
server nginx_lb_http unix@/run/nginx_lb_http.sock send-proxy weight 100 check
backend default_http_backend
mode http
option httpchk GET /health
server web1 192.168.1.201:80 weight 100 check
server web2 192.168.1.202:80 weight 100 check
### backend servers - HTTPS passthrough backends (your original approach)
backend default_https_passthrough
mode tcp
server nginx_lb_https unix@/run/nginx_lb_https.sock send-proxy weight 100 check
backend site1_https_passthrough
mode tcp
option ssl-hello-chk
server server1 192.168.1.101:443 weight 100 check
backend site2_https_passthrough
mode tcp
option ssl-hello-chk
server server2 192.168.1.102:443 weight 100 check
backend site3_https_passthrough
mode tcp
option ssl-hello-chk
server server3 192.168.1.103:443 weight 100 check
backend site4_https_passthrough
mode tcp
option ssl-hello-chk
server server4 192.168.1.104:443 weight 100 check
### backend servers - HTTP backends (for SSL terminated traffic)
backend site5_http_backend
mode http
option httpchk GET /health
# Backend servers receive plain HTTP after SSL termination
server web5a 192.168.1.105:80 weight 100 check
server web5b 192.168.1.106:80 weight 100 check
### Example: Multiple SSL certificates with termination
frontend https_multi_cert_terminated
# Bind with multiple certificates - HAProxy will select the right one based on SNI
bind 0.0.0.0:9443 ssl crt /etc/haproxy/certs/site6.example.com.pem crt /etc/haproxy/certs/site7.example.com.pem
mode http
option httplog
option forwardfor
# Security headers
http-response set-header Strict-Transport-Security "max-age=31536000; includeSubDomains"
# Route to different backends based on the original SNI/Host
use_backend site6_http_backend if { hdr(Host) -i site6.example.com }
use_backend site7_http_backend if { hdr(Host) -i site7.example.com }
backend site6_http_backend
mode http
balance roundrobin
option httpchk GET /api/health
server app6a 192.168.1.107:8080 weight 100 check
server app6b 192.168.1.108:8080 weight 100 check
backend site7_http_backend
mode http
balance leastconn
option httpchk GET /status
server app7a 192.168.1.109:3000 weight 100 check
server app7b 192.168.1.110:3000 weight 100
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
What about ssl certificates ? How do i handle them, i am having problems with those