我知道这个问题已经被问了一千次了,但我找到的所有答案都不起作用(对我来说,或者通常是这些问题的原始OP)。。。所以,我会尽我所能解释这个问题,希望我们能为我和其他曾经问过的人解决这个问题。
我的Nginx配置(去掉了很多其他不相关的东西)如下:
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;
}
}
我所要做的就是让Nginx捕获所有404,并通过location ~ '.php$
将它们发送回PHP-FPM,以便向用户显示自定义错误页面,但我总是得到标准的Nginx错误页面。
以下URL应全部显示mydomain.co.uk/error404.php
:的输出
- mydomain.co.uk/someNonExistantFile(与任何位置块都不匹配)
- mydomain.co.uk/someMissingFile.php(与.php文件位置块匹配,但该文件不存在)
但它们实际上显示了标准的Nginx 404页面。如果location ~ '.php$
向404返回不同的错误代码(例如5xx),那么我们不想参与其中,只需首先返回FastCGI返回的内容和标头即可。
我希望这是有道理的,希望有人能帮忙。提前谢谢。
编辑:我尝试将recursive_error_pages on;
添加到# Custom error pages
之后的行中,但这实际上会导致所有Nginx 404 Not Found
错误变为Nginx 500 Internal Server Error
错误。
编辑:添加其他文件:/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;
我想我可能根本不需要这两个!;-)
我终于解决了大部分问题。感谢大家的建议,并花时间写下答案。
问题是,我们的error404.php
也返回了带有404 Not Found标头的错误页面(使用header('HTTP/1.0 404 Not Found');
),然后Nginx在recursive_error_pages
关闭(默认)时拦截了这个错误,并且显示了自己的404页面。关闭fastcgi_intercept_errors
是解决方案。如果我们从错误文件中删除header('HTTP/1.0 404 Not Found');
,那么我们会得到错误,但200
显然不是我们想要的。
然而,解决了访问以.php
结尾的缺失页面的问题(因此它与位置块匹配,因为我们现在正在将标准的PHP-FPM响应返回到具有主体File not found.
的404标头的响应。我可以使用Nate的答案来解决这个问题,但我不想在那里指定所有文件名。我会为此寻找另一个解决方案,并在获得解决方案时将其发布在此处。
编辑:更完整的解决方案:
您需要在主php位置块中拦截错误(fastcgi_intercept_errors
是on
),然后为您的错误页面设置另一个不拦截错误的块。请参阅以下配置示例:
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;
}
}
这意味着,任何返回HTTP状态代码404的PHP页面都将忽略它们自己的内容(如果有的话),而是使用/http_errors/404.php
文件的内容。
我相信你所要求的是单用nginx是不可能的,除非你手动配置每个已知的.php文件。如果您可以列出已知的.php文件,那么您可以根据这些信息检测到预期的404。不是用正则表达式捕获所有的.php文件,而是指定已知的php文件:
用类似的东西替换location ~ '.php$ {}
location~^/(index|foo|bar|admin/index).php${}
然而,如果对一个所谓已知的php文件(如index.php)的请求返回404,即使你已经告诉nginx它应该存在,这也不会捕获你的机器外php-fpm生成的404。
完整的解决方案可能需要在您的nginx服务器前面有一个额外的服务器,该服务器检测到来自php-fpm/nginx的404响应,然后将另一个往返请求代理到您的后端以获取error404.php文件。您仍然会遇到error404.php文件本身也返回404的问题。我查看了Varnish,看看检测到的404是否可以向原始服务器生成第二个错误页面请求,但不幸的是,vcl_backend_error只能为404服务或重试请求。清漆或类似的东西可能有办法实现你想要的,但这并不容易。
我只能说,这就是为什么大多数人不在不同的机器上设置nginx和php-fpm的原因之一——这会引起这样的头痛。如果可能的话,您应该考虑将nginx和php-fpm放在同一台机器上,并对这些服务器进行负载平衡。无论你认为从分离它们中得到什么好处,都可能不值得额外的问题。
location ~ '.php$
块需要try_files来检查文件是否存在。如果不存在,则返回404,您的error404.php将接受它。
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;
root /var/www/xxxxxx;
# Custom error pages
error_page 404 = /error404.php;
# Any simple .php page
location ~ '.php$ {
try_files $uri =404;
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;
}
}
您的"root/var/www/xxxxxx;"应在"location~.php$"块的外部且在"error_page 404=/error404.php;"之前
如果您仍然无法从error404.php中获得所需的404页面,我建议您使用nginx.conf的标准副本开始调试,然后从那里自定义您的error_page,因为我们建议的方法有效。我对自己的服务器使用了相同的配置。
如果它仍然不起作用,那应该是一个上游问题。
您还需要检查
包括/etc/nginx/fastcgi.conf;包括fastcgi_params;
看看是否有任何流氓变量。那里可能有一些重复的变量。
调试的一般经验法则。减少你的变量。从一个简单的配置开始,然后按照自己的方式进行自定义。
的正确方式
error_page 404 /path/to/404.php;
我知道它是有效的,因为我最近构建了一个应用程序,这是错误处理的设置。请注意,我使用url重写从每个目录的请求中删除index.php
。通过这种方式,您可以设置错误脚本,将信息记录到数据库中,这对于难以调试的ajax错误非常有用:
error_page 500 /error/?t=500;
error_page 501 /error/?t=501;
error_page 502 /error/?t=502;
error_page 503 /error/?t=503;
error_page 400 /error/?t=400;
error_page 401 /error/?t=401;
error_page 403 /error/?t=403;
error_page 404 /error/?t=404;
error_page 405 /error/?t=405;
error_page 406 /error/?t=406;
error_page 413 /error/?t=413;
error_page 414 /error/?t=414;
error_page 418 /error/?t=418; # i'm a teapot