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:
- Stop your upstream service (port 5000 in this case)
- Make requests to various URLs including the root path
- 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";
}