When configuring custom error pages in nginx, developers often encounter situations where the error_page
directive appears to be silently ignored. The specific case we're examining involves returning a custom 400 error page for non-HTTPS requests, where despite correct configuration, the default nginx error page persists.
The initial configuration attempts show common patterns that don't work due to nginx's internal processing order:
server {
listen 80;
error_page 400 /400.html;
location = /400.html {
root /var/www/html;
}
return 400;
}
The issue stems from nginx processing the return
directive differently than expected. When using return
with a status code, nginx immediately terminates request processing and returns the specified status code without considering error_page
directives for that code.
Solution 1: Using rewrite with error_page
server {
listen 80;
root /var/www/html;
error_page 400 /400.html;
location = /400.html {
internal;
}
if ($scheme != "https") {
rewrite ^ /400.html;
return 400;
}
}
Solution 2: Using try_files approach
server {
listen 80;
root /var/www/html;
error_page 400 /400.html;
location / {
try_files /nonexistentfile @validate;
}
location @validate {
return 400;
}
}
For custom error pages to work with HTTP 400 status codes:
- The error page must be accessible through a location block marked as
internal
- Don't mix immediate
return
witherror_page
for the same status code - Consider using conditional logic (
if
) for HTTP/HTTPS validation - Ensure proper filesystem permissions for the error page
After implementing any solution, verify with:
curl -I http://yourserver
You should see both the 400 status code and your custom page content.
When implementing custom error pages in nginx, many developers encounter situations where the error_page
directive appears to be silently ignored, particularly with HTTP 400 errors. Here's why your configuration might not be working as expected:
server {
listen 80;
error_page 400 /400.html;
location = /400.html {
root /var/www/html;
}
return 400;
}
The issue presents several puzzling behaviors:
- The
return 400
statement works (changing the code produces different default nginx pages) - The custom 400.html file exists and serves correctly when accessed directly
- Yet the custom page never appears when triggered by the return statement
The root cause lies in nginx's request processing phases. The return
directive in the server context immediately terminates processing, bypassing the error_page handler. This differs from errors that occur during request processing (like invalid requests or upstream errors).
Here are three proven approaches to make custom 400 pages work:
Method 1: Trigger via Invalid Request
server {
listen 80;
server_name example.com;
root /var/www/html;
error_page 400 /400.html;
location = /400.html {
internal;
}
# Force 400 by requiring HTTPS
if ($scheme != "https") {
return 400;
}
}
Method 2: Use location-based Return
server {
listen 80;
root /var/www/html;
error_page 400 /400.html;
location / {
return 400;
}
location = /400.html {
internal;
}
}
Method 3: Rewrite + Error Page
server {
listen 80;
root /var/www/html;
error_page 400 /400.html;
location / {
rewrite ^ /invalid-request;
}
location = /400.html {
internal;
}
}
- Always mark error page locations as
internal
- Place error pages outside protected locations
- Test with
curl -v http://yourserver
to verify headers - Check nginx error logs (
/var/log/nginx/error.log
) for troubleshooting
For more sophisticated error handling, consider using named locations:
server {
listen 80;
root /var/www/html;
error_page 400 @custom400;
location @custom400 {
root /var/www/errors;
try_files /400.html =400;
}
location / {
return 400;
}
}