Nginx - 将所有404错误传递回PHP-FPM以进行自定义错误页面处理

时间:2014-04-16 10:10:52

标签: nginx http-status-code-404 php

我知道这已被问过一千次,但我发现的所有答案根本不起作用(对我来说或通常是那些问题的原始OP)......所以,我&# 39;尽量尽可能地解释问题,希望我们可以让它为我和之前提出过要求的其他人工作。

我的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错误页面。

以下网址都应显示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;

我想我可能也不需要这两个! ; - )

5 个答案:

答案 0 :(得分:14)

我终于完成了大部分工作。感谢大家提供的建议并花时间写答案。

问题是我们的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响应恢复为404标题的那些身体File not found.。我可以使用Nate's answer解决这个问题,但我不需要在那里指定所有文件名。我会为此寻找另一个解决方案当我得到它时,把它贴在这里。

编辑:更完整的解决方案:

您需要拦截主要php位置区块中的错误(fastcgi_intercept_errorson),然后为您的错误页面设置另一个阻止它们的区块。请参阅此配置示例:

    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文件的内容。

答案 1 :(得分:2)

我相信你所要求的是单独的nginx是不可能的 - 除非你手动配置每个已知的.php文件。如果您可以列出已知的.php文件,则可以根据此信息检测到预期的404。而不是使用正则表达式捕获所有.php文件,而是指定已知的php文件:

location ~ \.php$ {}替换为:

  

location~ ^ /(index | foo | bar | admin / index).php $ {}

但是,如果对 index.php 这样的知名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保存在同一台机器上并负载平衡这些服务器。无论你认为从分离它们中得到什么好处,都可能不值得额外的问题。

答案 2 :(得分:0)

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;
    }
}

答案 3 :(得分:0)

你的&#34; root / var / www / xxxxxx;&#34;应该在&#34;位置<。>外面〜.php $&#34;阻止和之前&#34; error_page 404 = /error404.php;&#34;

如果您仍然无法从error404.php获取所需的404页面,我建议您使用nginx.conf的标准副本开始调试,然后从那里自定义您的error_page,因为我们的建议有效。我为自己的服务器使用了相同的配置。

如果它仍然不起作用,它应该是一个上游问题。

您还想查看

包括/etc/nginx/fastcgi.conf; 包括fastcgi_params;

查看是否有任何恶意变量。那里可能有一些重复的变量。

调试的一般经验法则。减少变量。从简单的配置开始,逐步完成自定义。

答案 4 :(得分:0)

正确的方法

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