How to Properly Configure Nginx error_page for 502 Bad Gateway Errors: A Complete Guide


12 views

When dealing with Nginx reverse proxy configurations, handling 502 Bad Gateway errors properly is crucial for maintaining a good user experience during upstream server outages. The original configuration attempts to serve a maintenance page when the backend (upstream) service becomes unavailable, but fails to trigger for the root path (GET /).

The configuration shown has several potential issues that might prevent the error_page directive from working as expected:

server {
    listen 0.0.0.0;
    server_name dev.host.com;

    location / {
        include /etc/nginx/proxy.conf;
        proxy_pass http://127.0.0.1:5000;
        proxy_redirect default;
        error_page 502 =200 @maintenance;
    }
    # ... rest of config ...
}

Key observations about why this might fail:

  • The error_page directive is placed inside a location block, which might not catch all 502 errors
  • The proxy_next_upstream mechanism might interfere with error handling
  • There might be missing proxy parameters that affect error handling

Here's an improved configuration that handles 502 errors more reliably:

server {
    listen 80;
    server_name dev.host.com;

    # Global error handling for all locations
    error_page 502 503 504 /maintenance.html;
    
    location = /maintenance.html {
        root /path/to/static/offline/files;
        internal;
    }

    location / {
        include /etc/nginx/proxy.conf;
        proxy_pass http://127.0.0.1:5000;
        proxy_redirect default;
        
        # Important proxy settings for proper error handling
        proxy_next_upstream off;
        proxy_intercept_errors on;
    }

    location ^~ /(img|js|css)/ {
        root /path/to/application/assets;
        expires max;
        error_page 404 =302 /;
    }
}

The solution incorporates several best practices:

  • Global error_page directive: Placed at server level to catch all 502 errors
  • proxy_next_upstream off: Prevents Nginx from retrying failed requests
  • proxy_intercept_errors on: Allows Nginx to handle proxy errors
  • Dedicated maintenance page location: Simplified static file serving

For more complex maintenance scenarios, you can still use named locations:

server {
    # ... other config ...
    
    error_page 502 = @maintenance;
    
    location @maintenance {
        root /path/to/static/offline/files;
        try_files /maintenance.html =503;
    }
}

To verify your error handling works:

  1. Stop your upstream service (port 5000 in this case)
  2. Make requests to various URLs including the root path
  3. Check response codes and content with curl: curl -I http://dev.host.com/

Remember to test with different request types (static files, API calls, etc.) to ensure consistent behavior.

  • Not setting proxy_intercept_errors on
  • Having proxy_next_upstream enabled with multiple upstream servers
  • Incorrect root path for maintenance files
  • Overly complex try_files directives in error handling

# Current behavior:
# When upstream server (http://127.0.0.1:5000) is down:
# - Requests to / return default Nginx 502 page (undesired)
# - Other static assets still work fine

After examining the provided config, I noticed several potential issues:

location / {
    include /etc/nginx/proxy.conf;
    proxy_pass http://127.0.0.1:5000;
    proxy_redirect default;
    
    # This error_page directive might not work as expected
    error_page 502 =200 @maintenance;
}

The main issue stems from how Nginx processes error_page directives with proxy_pass. When the upstream is completely unreachable, Nginx might handle the error at a different processing stage than expected.

Here's the corrected configuration that properly handles 502 errors:

server {
    listen 0.0.0.0;
    server_name dev.host.com;

    # Global error handler that catches all 502s
    error_page 502 @maintenance;

    location / {
        include /etc/nginx/proxy.conf;
        proxy_pass http://127.0.0.1:5000;
        proxy_redirect default;
        
        # Important addition for proper error handling
        proxy_intercept_errors on;
    }

    location ^~ /(img|js|css)/ {
        root /path/to/application/assets;
        expires max;
        break;
        error_page 404 =302 /;
    }

    location @maintenance {
        root /path/to/static/offline/files;
        try_files $uri $uri/ /index.html =503;
    }
}
  • Moved the error_page directive to server level for global coverage
  • Added proxy_intercept_errors on to ensure proper error handling
  • Removed the =200 code conversion which might cause issues

To verify it works:

# Simulate upstream failure
$ sudo service yourapp stop

# Test different scenarios
$ curl -I http://dev.host.com/          # Should show maintenance page
$ curl -I http://dev.host.com/js/app.js # Should redirect if missing

For more complex setups, consider adding:

location @maintenance {
    # Custom headers
    add_header Retry-After 3600;
    add_header Cache-Control no-cache;
    
    # Different response codes
    return 503 "System Maintenance";
}