Optimal Web Infrastructure Stack: Comparing nginx, Varnish, HAProxy for High-Performance Backend Routing and Caching


2 views

When designing high-traffic web infrastructure, I've encountered numerous debates about combining multiple reverse proxies. Let's examine why engineers might layer these components despite feature overlaps:


# Typical multi-layer configuration example
client → HAProxy (TCP load balancing)
       → Varnish (HTTP caching)
       → nginx (SSL termination/static content)
       → application servers

HAProxy shines in these scenarios:

  • Layer 4 load balancing with minimal overhead
  • Advanced health checking mechanisms
  • TCP connection multiplexing

# HAProxy configuration snippet for TLS passthrough
frontend https-in
    bind *:443
    mode tcp
    default_backend varnish_servers

backend varnish_servers
    mode tcp
    server varnish1 192.168.1.10:80 check
    server varnish2 192.168.1.11:80 check backup

While nginx can cache, Varnish offers:

  • Millisecond-level cache purging via VCL
  • Edge Side Includes (ESI) support
  • Built-in support for grace/stale content

# Varnish VCL example for cache variation
sub vcl_backend_response {
    if (bereq.url ~ "\.(jpg|png|gif)$") {
        set beresp.ttl = 1h;
        set beresp.http.Cache-Control = "public, max-age=3600";
    }
    elseif (bereq.url ~ "\.(css|js)$") {
        set beresp.ttl = 7d;
    }
}

In our stack, nginx handles:

  • TLS termination with OCSP stapling
  • Broti/Gzip compression
  • Static file serving with zero-copy sendfile

# nginx configuration for optimized TLS
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384';
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;

For many deployments, nginx alone suffices with:

  • ngx_http_proxy_module for load balancing
  • ngx_http_cache_purge module
  • Lua scripting via OpenResty

The decision to combine these tools depends on:


if (traffic > 10K RPS && need_subsecond_purges) {
    use Varnish;
} else if (need_L4_load_balancing) {
    use HAProxy;
} else {
    nginx_alone_may_suffice();
}

In modern web deployments, it's common to see complex stacks with multiple reverse proxies and caching layers. While this provides feature flexibility, it introduces operational complexity that needs justification.

Each technology shines in specific scenarios:

# Nginx configuration snippet for SSL termination
server {
    listen 443 ssl;
    server_name example.com;
    ssl_certificate /path/to/cert.pem;
    ssl_certificate_key /path/to/key.pem;
    # Compression and caching directives...
}

Nginx excels at SSL termination and static content serving, while Varnish provides advanced caching logic:

# Varnish VCL for cache control
sub vcl_backend_response {
    if (bereq.url ~ "\.(jpg|png|gif|webp)$") {
        set beresp.ttl = 1w;
    }
    if (bereq.url ~ "\.(css|js)$") {
        set beresp.ttl = 3d;
    }
}

Common production architectures include:

  • HAProxy → Varnish → Nginx → Application (for high-availability needs)
  • Cloudflare → Nginx → Varnish → Application (CDN-enhanced stack)
  • AWS ALB → HAProxy → Application (cloud-native simplified stack)

Each additional layer introduces latency. Benchmarks show:

# Sample wrk benchmark results
Single Nginx: 45,000 req/s
Nginx+Varnish: 38,000 req/s (but better cache hit ratio)
Full stack: 32,000 req/s (with HAProxy health checks)

Consider these operational factors:

  • Configuration synchronization across layers
  • Debugging request flow through multiple components
  • Security patch management for each component

Newer solutions like Envoy and Traefik combine multiple functionalities:

# Envoy configuration combining LB and caching
resources:
- "@type": type.googleapis.com/envoy.config.route.v3.RouteConfiguration
  name: local_route
  virtual_hosts:
  - name: backend
    domains: ["*"]
    routes:
    - match: { prefix: "/" }
      route:
        cluster: backend_service
        cache:
          disabled: false
          max_bytes: 52428800

Ultimately, the right architecture depends on your specific traffic patterns, performance requirements, and team expertise.