How to Configure Nginx Fallback Error Page When Proxy Destination is Unavailable


4 views

When setting up Nginx as a reverse proxy, you might encounter situations where the backend service becomes temporarily unavailable. In such cases, instead of showing generic error messages or blank pages, it's better to display a custom maintenance page to your users.

Here's what's happening with the current setup:

server {
    listen       80;
    server_name  "";

    location / {
        proxy_pass  http://127.0.0.1:9080;
        proxy_set_header    Host $host;
        proxy_set_header    X-Real-IP   $remote_addr;
        proxy_set_header    X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_connect_timeout 1;
        proxy_next_upstream error timeout http_500 http_502 http_503 http_504 http_404;
        proxy_intercept_errors on;
    }

    error_page 501 502 503 @maintenance;
    location @maintenance {
            root   /locust/www/fallback/htdocs;
            index  index.html index.htm;
    }
}

The configuration has several problems preventing the fallback from working:

  • The error_page directive only handles 501, 502, and 503 status codes
  • Nginx might be returning different error codes when the backend is down
  • The root path needs proper verification

Here's a corrected configuration that properly handles backend unavailability:

server {
    listen 80;
    server_name example.com;

    # Maintenance page location
    location /maintenance.html {
        root /var/www/html;
        internal;
    }

    location / {
        proxy_pass http://127.0.0.1:9080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        
        # Timeout settings
        proxy_connect_timeout 1s;
        proxy_read_timeout 1s;
        proxy_send_timeout 1s;
        
        # Error handling
        proxy_intercept_errors on;
        error_page 500 502 503 504 /maintenance.html;
    }
}

The working solution includes several important changes:

  1. Added proper timeout settings for all proxy operations
  2. Expanded the error codes to include common proxy failures (500, 502, 503, 504)
  3. Simplified the maintenance page handling using a direct file reference
  4. Made the maintenance page accessible only internally

To verify it works:

# Test configuration
sudo nginx -t

# Simulate backend failure
sudo service your-backend-service stop

# Check response
curl -I http://your-domain.com

For more complex scenarios, consider:

# Using regex to match multiple error codes
error_page 5xx /maintenance.html;

# Custom error page for different status codes
error_page 502 /502.html;
error_page 503 /503.html;

# Serving different maintenance pages based on URI
location ~ ^/(api|admin) {
    proxy_pass http://127.0.0.1:9080;
    error_page 502 /api-maintenance.html;
}
  • Ensure your maintenance page doesn't exceed Nginx buffer limits
  • Verify file permissions on your maintenance HTML files
  • Don't forget to include proper MIME types for your error pages
  • Test with different error scenarios (timeout, connection refused, etc.)

When implementing error handling:

  • Keep maintenance pages small (under 10KB recommended)
  • Consider caching maintenance pages in memory
  • Avoid external resources in maintenance pages (CSS, JS, images)
  • Use browser cache headers for static maintenance pages

When setting up Nginx as a reverse proxy, it's common to encounter situations where the backend service becomes temporarily unavailable. While Nginx provides robust proxy features, properly handling these failure scenarios requires specific configuration.

The original configuration attempts to handle this with:

error_page 501 502 503 @maintenance;
location @maintenance {
    root   /locust/www/fallback/htdocs;
    index  index.html index.htm;
}

The key issue lies in the HTTP status codes being intercepted. When a backend service is unavailable, Nginx typically returns a 502 (Bad Gateway) or 504 (Gateway Timeout) error. However, the configuration only handles 501, 502, and 503.

Here's a working configuration that properly handles all common proxy failure scenarios:

server {
    listen       80;
    server_name  "";

    location / {
        proxy_pass  http://127.0.0.1:9080;
        proxy_set_header    Host $host;
        proxy_set_header    X-Real-IP   $remote_addr;
        proxy_set_header    X-Forwarded-For $proxy_add_x_forwarded_for;
        
        # Timeout settings
        proxy_connect_timeout 1s;
        proxy_read_timeout 2s;
        proxy_send_timeout 2s;
        
        # Error handling
        proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
        proxy_intercept_errors on;
    }

    # Handle all possible proxy failure codes
    error_page 500 502 503 504 /maintenance.html;
    
    location = /maintenance.html {
        root /locust/www/fallback/htdocs;
        internal;
    }
}

The improved solution includes:

  • More comprehensive timeout settings
  • Broader range of handled error codes (500-504)
  • Simplified error page handling using a direct path
  • internal directive to prevent direct access

To verify it works:

  1. Start Nginx with the new configuration
  2. Make sure your backend service is running
  3. Stop the backend service
  4. Access your Nginx server - you should see the maintenance page

For production environments, you might want to add:

# Cache control for maintenance page
location = /maintenance.html {
    add_header Cache-Control "no-store";
    expires 0;
    # ... rest of config ...
}

This ensures browsers won't cache the maintenance page when the service comes back online.