How to Configure Nginx as a High Availability Reverse Proxy with Failover (No Load Balancing)


2 views

When building mission-critical web applications, having a failover mechanism is crucial. Many developers mistakenly think they need complex load balancing setups when they actually just need simple high availability. The requirement here is clear: route all traffic to Server A, and only failover to Server B when A becomes unavailable.

Here's the fundamental upstream configuration that achieves this:

upstream backend {
    server primary.example.com:80 max_fails=3 fail_timeout=30s;
    server backup.example.com:80 backup;
}

The magic happens with these directives:

  • max_fails: Number of unsuccessful attempts before marking server as unavailable
  • fail_timeout: Time period for max_fails and duration server is marked as down
  • backup: Marks this server as backup - only used when primary fails

Here's a full configuration you can drop into your nginx.conf:

http {
    upstream myapp {
        server 192.168.1.100:8080 max_fails=3 fail_timeout=30s;
        server 192.168.1.101:8080 backup;
    }

    server {
        listen 80;
        server_name example.com;

        location / {
            proxy_pass http://myapp;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
    }
}

To verify your setup works:

  1. Start both backend servers
  2. Make requests - all should go to primary
  3. Stop primary server
  4. After fail_timeout period, requests should automatically switch to backup
  5. Bring primary back up - traffic should return automatically

For better visibility, add these to your location block:

proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
proxy_intercept_errors on;

This ensures nginx properly handles various failure scenarios.


When setting up a reverse proxy with Nginx, you might need a failover mechanism where traffic is automatically redirected to a backup server if the primary server fails. Unlike load balancing, which distributes traffic across multiple servers, failover focuses on high availability by using a secondary server only when the primary is unavailable.

Here's a simple Nginx configuration to achieve failover without load balancing:


http {
    upstream backend {
        server primary.example.com:80 max_fails=1 fail_timeout=5s;
        server backup.example.com:80 backup;
    }

    server {
        listen 80;
        location / {
            proxy_pass http://backend;
            proxy_next_upstream error timeout http_500 http_502 http_503 http_504;
        }
    }
}

The important directives in this setup are:

  • max_fails: Number of unsuccessful attempts before marking server as unavailable
  • fail_timeout: Time period during which max_fails must occur
  • backup: Marks the server as backup (only used when primary fails)
  • proxy_next_upstream: Specifies which errors should trigger failover

For more reliable failover, you can implement active health checks:


http {
    upstream backend {
        zone backend 64k;
        server primary.example.com:80 max_fails=3 fail_timeout=30s;
        server backup.example.com:80 backup;
    }

    server {
        listen 80;
        location / {
            proxy_pass http://backend;
            health_check interval=5s fails=3 passes=1 uri=/health;
            proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
        }
    }
}

If your application requires session persistence, you'll need to handle this carefully during failover:


http {
    upstream backend {
        ip_hash;
        server primary.example.com:80 max_fails=3 fail_timeout=30s;
        server backup.example.com:80 backup;
    }
}

Always test your failover configuration:

  1. Start Nginx with your configuration
  2. Verify traffic goes to primary server
  3. Simulate failure (stop web service or block port)
  4. Verify traffic fails over to backup
  5. Restore primary and verify traffic returns

Watch out for these issues:

  • Insufficient timeout values causing premature failover
  • Backup server not properly synchronized with primary
  • DNS caching affecting failover behavior
  • Missing proper health check endpoints

Add these to your configuration for better visibility:


http {
    log_format upstream_log '$remote_addr - $remote_user [$time_local] '
                           '"$request" $status $body_bytes_sent '
                           '"$http_referer" "$http_user_agent" '
                           'rt=$request_time uct="$upstream_connect_time" '
                           'uht="$upstream_header_time" urt="$upstream_response_time" '
                           'upstream_addr=$upstream_addr';

    server {
        access_log /var/log/nginx/access.log upstream_log;
        error_log /var/log/nginx/error.log warn;
    }
}