Learn HAProxy - Layer 4 Proxy & TCP Routing
Episode 12 of 23

Learn HAProxy - Layer 4 Proxy & TCP Routing

This episode takes HAProxy down to layer 4: TCP mode for proxying raw protocols like databases and gRPC, the fundamental differences from HTTP mode, and SNI inspection and TLS passthrough for routing without reading traffic contents.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Introduction

Not every service speaks HTTP. Databases, Redis, memcached, and many legacy protocols need a proxy that doesn't interfere with the byte contents. That's what HAProxy's very capable TCP mode is for.

Episode 12 dissects TCP mode: how it differs from HTTP, how to proxy raw services, and how SNI inspection enables domain-name-based routing without opening encrypted data. These capabilities let HAProxy serve almost any protocol.

TCP Mode vs HTTP Mode

The Capability Limits of Each Mode

A quick comparison to remember:

  • TCP mode: HAProxy only forwards bytes. Low overhead, suitable for any protocol.
  • HTTP mode: HAProxy understands requests and responses, can route based on contents, modify headers, and maintain sessions.

The main decision is simple: if you don't need to see the protocol contents, use TCP. It's faster and safer because it doesn't interpret data.

Consequences of the Mode Choice

In TCP mode, HTTP-dependent features aren't available:

  • No http-request, no path-based use_backend, no header rewriting.
  • Routing can only use network metadata: source IP, destination port, or SNI.
  • Session affinity can use a stick table with src, not cookies.
TCP mode for raw services
frontend redis_front
    bind *:6379
    mode tcp
    default_backend redis_back
 
backend redis_back
    balance leastconn
    server redis1 10.0.0.21:6379 check
    server redis2 10.0.0.22:6379 check

The mode tcp directive in both sections indicates raw byte forwarding. The default TCP health check just opens a connection and closes it again.

Proxying Raw Services

PostgreSQL Database

Databases are the most common example. Client applications connect to HAProxy, then the connection is forwarded to the primary and replicas:

Proxying PostgreSQL
frontend pg_front
    bind *:5432
    mode tcp
    default_backend pg_back
 
backend pg_back
    balance source
    option tcp-check
    tcp-check connect
    tcp-check send "select 1;\n"
    tcp-check expect string "1"
    server pg1 10.0.0.31:5432 check
    server pg2 10.0.0.32:5432 check

option tcp-check enables layered TCP health checks: HAProxy sends select 1; and expects an answer containing 1. This is a much more meaningful health check than merely opening a connection.

Redis and Long-Lived Connections

For Redis, use leastconn so long-held connections don't pile up on one server:

Proxying Redis
backend redis_back
    balance leastconn
    option tcp-check
    tcp-check connect
    tcp-check send PING\r\n
    tcp-check expect string +PONG
    server redis1 10.0.0.41:6379 check
    server redis2 10.0.0.42:6379 check

tcp-check send PING\r\n and tcp-check expect string +PONG verify that Redis really responds before it's considered healthy.

gRPC in TCP Mode

If you don't need service-method-based routing, gRPC can be proxied purely at layer 4 with HTTP/2 multiplexing forwarded as-is:

Proxying gRPC at layer 4
frontend grpc_front
    bind *:8443
    mode tcp
    default_backend grpc_back
 
backend grpc_back
    balance leastconn
    server grpc1 10.0.0.51:8443 check
    server grpc2 10.0.0.52:8443 check

TCP mode keeps the HTTP/2 connection intact from client to server. There's no buffering or interpretation that could interfere with the streams.

SNI Inspection and TLS Passthrough

Understanding TLS Passthrough

TLS passthrough means HAProxy doesn't terminate encryption: the TLS handshake happens directly between the client and the backend. HAProxy only sees the unencrypted part, namely the server name in the handshake (SNI).

The benefits: private keys never live on HAProxy, and traffic stays encrypted end-to-end.

SNI-Based Routing

SNI is inspected in TCP mode to choose the backend:

SNI-based routing
frontend tls_front
    bind *:443
    mode tcp
 
    acl sni_db req.ssl_sni -i db.example.com
    acl sni_app req.ssl_sni -i app.example.com
 
    use_backend db_back if sni_db
    use_backend app_back if sni_app
    default_backend app_back
 
backend db_back
    server db1 10.0.0.61:443 check
 
backend app_back
    server app1 10.0.0.62:443 check

acl sni_db req.ssl_sni -i db.example.com matches the server name requested by the client. Make sure the frontend enables SNI parsing with accept-proxy or simply in TCP mode, then use_backend db_back if sni_db routes the traffic.

Verifying with openssl

Check the SNI sent by the client
openssl s_client -connect localhost:443 -servername db.example.com \
  </dev/null 2>/dev/null | openssl x509 -noout -subject

openssl s_client -connect localhost:443 -servername db.example.com opens a TLS handshake with a specific SNI. If routing is correct, the certificate returned should belong to the database backend.

Design Considerations

When Passthrough and When Termination

A practical guide:

  • Passthrough: maximum security, the backend handles TLS, no content inspection. Suited for regulatory requirements and highest performance.
  • Termination: HAProxy holds the certificate, can rewrite, route on HTTP content, and manage certificates centrally. Suited for most web apps.

Many architectures mix both: termination at the edge for the web, passthrough for internal services.

Test a TCP connection
nc -zv localhost 6379

nc -zv localhost 6379 tests whether the TCP port is open. If the connection succeeds, TCP mode is working; then test the protocol directly with the respective clients.

Closing

Episode 12 opens the world of layer 4: TCP mode for raw protocols, protocol-based health checks with tcp-check, and SNI and TLS passthrough for secure, efficient routing.

Key takeaways:

  • mode tcp forwards bytes; mode http understands protocol contents.
  • Databases and Redis use option tcp-check for meaningful health checks.
  • balance leastconn for long-lived connections like Redis and gRPC.
  • TLS passthrough keeps traffic encrypted end-to-end.
  • req.ssl_sni enables domain-name-based routing without opening contents.

In the next episode we'll cover high availability & clustering — active/passive setups with VRRP and Keepalived, service discovery integration like Consul and DNS, and testing failover and recovery.

Learn HAProxy - Layer 4 Proxy & TCP Routing | Learn HAProxy