Nginx - Pass all 404 errors back to PHP-FPM for custom error page processing
http-status-code-404, nginx, php
Solution
I've finally worked most of it out. Thanks everyone for your tips and putting the time in to write answer.
The issue is that our `error404.php` was returning our error page with an 404 Not Found header too (using `header('HTTP/1.0 404 Not Found');`) and Nginx was then intercepting this error as `recursive_error_pages` is off (default) and it was showing its own 404 page. Switching off `fastcgi_intercept_errors` is the solution. If we remove the `header('HTTP/1.0 404 Not Found');` from the error file then we'd get the error but with a `200` which is obviously not what we want.
This does not, however, solve the problem of accessing a missing page which ends with `.php` (so it matches the location block as we are now getting back the standard PHP-FPM response to those of a 404 header with the body `File not found.`. I could use Nate's answer to get around this, but I'd rather not need to specify all the file names there. I'll look for another solution for this and post it here when I get one.
EDIT: A more full solution:
You need to intercept errors in your main php location block (`fastcgi_intercept_errors` is `on`) and then have another block for your error pages where you don't intercept them. See this config example:
server {
listen 80;
listen 443 ssl;
server_name mydomain.co.uk;
ssl_certificate /xxxxxxx.crt;
ssl_certificate_key /xxxxxxx.key;
# Custom error pages
recursive_error_pages off;
error_page 404 = /http_errors/404.php;
# error_page 500 501 502 503 504 = /error5xx.php; # Not sure about this yet!
# Any simple .php page
location ~ \.php$ {
root /var/www/xxxxx;
include /etc/nginx/fastcgi.conf;
fastcgi_pass phpfastcgiservers;
include fastcgi_params;
fastcgi_intercept_errors on;
}
# Handling error pages
location ^~ /http_errors/ {
internal;
root /var/www/xxxxx;
include /etc/nginx/fastcgi.conf;
fastcgi_pass phpfastcgiservers;
include fastcgi_params;
fastcgi_intercept_errors off;
}
}
This will mean that any of your PHP pages which return an HTTP status code of 404 will have their own content (if any) ignored and the content of your `/http_errors/404.php` file will be used instead.
Problem
I know this has been asked a thousand times, but all of the answers I'm found simply don't work (for me or usually the original OP of those questions)... So, I'll try to explain the problem as best as I possibly can and hopefully we can get it working for me and for others who have asked before. My Nginx config (with lots of other irrelevant stuff removed) is as follows: ``` http { # Config from here removed server { listen 80; listen 443 ssl; server_name mydomain.co.uk; ssl_certificate /xxxxxxx.crt; ssl_certificate_key /xxxxxxx.key; # Custom error pages root /var/www/viovet_frontend; error_page 404 = /error404.php; # Any simple .php page location ~ \.php$ { root /var/www/xxxxxx; #index index.php index.html; include /etc/nginx/fastcgi.conf; fastcgi_pass phpfastcgiservers; include fastcgi_params; fastcgi_intercept_errors on; } # Lots more config and re-write rules here removed } upstream phpfastcgiservers { server xxxxx1:9001; server xxxxx2:9001; server xxxxx3:9001; fair; } } ``` All I'm trying to do is get Nginx to catch all 404s and send them back to PHP-FPM via `location ~ \.php$` to have a custom error page shown to the user, but I always get the standard Nginx error page. The following urls should all show the output of `mydomain.co.uk/error404.php`: - mydomain.co.uk/someNonExistantFile (didn't match any location blocks) - mydomain.co.uk/someMissingFile.php (matched the .php file location block but the file doesn't exist) But they actually show the standard Nginx 404 page. If the `location ~ \.php$` returns a different error code to 404 (e.g 5xx) then we don't want to get involved, just return the content and headers that FastCGI returned in the first place. I hope that makes sense and that someone can help. Thank you in advance. EDIT: I have tried adding `recursive_error_pages on;` to the line after `# Custom error pages` but this actually causes all Nginx `404 Not Found` errors to become Nginx `500 Internal Server Error` errors. EDIT: Adding other files: /etc/nginx/fastcgi.conf ``` fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param QUERY_STRING $query_string; fastcgi_param REQUEST_METHOD $request_method; fastcgi_param CONTENT_TYPE $content_type; fastcgi_param CONTENT_LENGTH $content_length; fastcgi_param SCRIPT_NAME $fastcgi_script_name; fastcgi_param REQUEST_URI $request_uri; fastcgi_param DOCUMENT_URI $document_uri; fastcgi_param DOCUMENT_ROOT $document_root; fastcgi_param SERVER_PROTOCOL $server_protocol; fastcgi_param HTTPS $https if_not_empty; fastcgi_param GATEWAY_INTERFACE CGI/1.1; fastcgi_param SERVER_SOFTWARE nginx/$nginx_version; fastcgi_param REMOTE_ADDR $remote_addr; fastcgi_param REMOTE_PORT $remote_port; fastcgi_param SERVER_ADDR $server_addr; fastcgi_param SERVER_PORT $server_port; fastcgi_param SERVER_NAME $server_name; # PHP only, required if PHP was built with --enable-force-cgi-redirect fastcgi_param REDIRECT_STATUS 200; ``` fastcgi_params ``` fastcgi_param QUERY_STRING $query_string; fastcgi_param REQUEST_METHOD $request_method; fastcgi_param CONTENT_TYPE $content_type; fastcgi_param CONTENT_LENGTH $content_length; fastcgi_param SCRIPT_NAME $fastcgi_script_name; fastcgi_param REQUEST_URI $request_uri; fastcgi_param DOCUMENT_URI $document_uri; fastcgi_param DOCUMENT_ROOT $document_root; fastcgi_param SERVER_PROTOCOL $server_protocol; fastcgi_param HTTPS $https if_not_empty; fastcgi_param GATEWAY_INTERFACE CGI/1.1; fastcgi_param SERVER_SOFTWARE nginx/$nginx_version; fastcgi_param REMOTE_ADDR $remote_addr; fastcgi_param REMOTE_PORT $remote_port; fastcgi_param SERVER_ADDR $server_addr; fastcgi_param SERVER_PORT $server_port; fastcgi_param SERVER_NAME $server_name; # PHP only, required if PHP was built with --enable-force-cgi-redirect fastcgi_param REDIRECT_STATUS 200; ``` I guess I probably don't need both of these anyway! ;-)