Nginx - 如何禁用 error_page 重写

Nginx - 如何禁用 error_page 重写

我目前在 Fedora 25 Server Edition x86_64 上使用 Nginx 1.10.2 托管几个小型静态网站。

我已经将 Nginx 配置为假定.html没有文件扩展名()的请求,并且如果请求则try_files重定向(永久rewrite)到.html-less 版本的 URL 。.*\.html

我也有自定义错误页面。据我目前所知,该error_page指令与重写配合得不好,因为我被应该返回正常错误消息的页面的重定向循环所困扰……我相信这是所有相关的配置:

server {
    [...]

    try_files  $uri  $uri.html  $uri/index.html  =404;

    error_page  400  /errors/400.html;
    error_page  401  /errors/401.html;
    error_page  403  /errors/403.html;
    error_page  404  /errors/404.html;
    error_page  408  /errors/408.html;
    error_page  500  /errors/500.html;
    error_page  503  /errors/503.html;

    # Remove "index" from URL if requesting the front page.
    rewrite  "^/index(\.html)?$"    "/"    permanent;

    # Strip .html file extension, trailing slash, or "index.html".
    rewrite  "/(.+)(\.html|/|/index.html)$"  "/$1"  permanent;

    [...]
}

以下是我思考Nginx 正在做:

  1. 客户请求/fake-page
  2. 查找/fake-page、、/fake-page.html/fake-page/index.html
  3. 当这些都不匹配时,内部重定向以显示错误 404 页面。
  4. 但随后/errors/404.html带有标志的 .html 被剥离permanent ,导致 301 用户重定向。

我尝试了最后一行的几种变体rewrite,甚至将其放在一个location ^~ /errors/ {}块中(我思考应该意味着重写仅适用于不是在 /errors/ 目录下)。但我所做的一切都导致了错误 404 永久重定向到 404,然后不会返回实际的 404 状态 ---或者它最终陷入了重定向循环。

答案1

我建议您包装rewrites内部location块,否则很难控制它们的全局影响力。

此示例似乎适用于您在问题中发布的代码片段:

server {
    root /path/to/root;

    location / {
        # Remove "index" from URL if requesting the front page.
        rewrite  "^/index(\.html)?$"    "/"    permanent;

        # Strip .html file extension, trailing slash, or "index.html".
        rewrite  "/(.+)(\.html|/|/index.html)$"  "/$1"  permanent;

        try_files  $uri  $uri.html  $uri/index.html  =404;
    }

    error_page  400  /errors/400.html;
    error_page  401  /errors/401.html;
    error_page  403  /errors/403.html;
    error_page  404  /errors/404.html;
    error_page  408  /errors/408.html;
    error_page  500  /errors/500.html;
    error_page  503  /errors/503.html;

    location /errors/ {
    }
}

答案2

这是一个难题,但最终它按要求完成了工作:

server {

    [...]

    root /path/to/root;
    set $docroot "/path/to/root";
    error_page  400  /errors/400.html;
    error_page  401  /errors/401.html;
    error_page  403  /errors/403.html;
    error_page  404  /errors/404.html;
    error_page  408  /errors/408.html;
    error_page  500  /errors/500.html;
    error_page  503  /errors/503.html;

    location = /errors/404.html {
        root $docroot;
        internal;
    }

    location ~ ^/index(\.html)?$
    {
        return 301 "/";
        break;   
    }

     location ~ ^/$
    {
        try_files  $uri  $uri.html  $uri/index.html  =404;
        break;
    }

    location ~ ^/(.+)(\.html|/|/index.html)$
    {
        if (-f $docroot/$1) {
            return 301  "/$1";
            break;
        }

        if (-f $docroot/$1.html) {
            return 301  "/$1";
            break;
        }

        if (-f $docroot/$1/index.html) {
            return 301  "/$1";
            break;
        }

        try_files missing_file  =404; 
    }

    location ~ ^/(.+)(?!(\.html|/|/index.html))$
    {
        try_files  $uri  $uri.html  $uri/index.html  =404;    
    }

    [...]
}

稍后我会尝试通过评论来扩展这一点;)

答案3

我找到了一个更简单的解决方案,受到@Anubioz 的回答的启发。

server {
    [...]

    # This part's pretty common.
    # Assume ".html" or ".../index.html" if the original request doesn't
    # match any real files. Return error 404 if still can't find any
    # matching files.
    try_files  $uri  $uri.html  $uri/index.html  =404;

    # Match any resulting HTTP status codes with the custom error pages
    # that I designed.
    error_page  400  /errors/400.html;
    error_page  401  /errors/401.html;
    error_page  403  /errors/403.html;
    error_page  404  /errors/404.html;
    error_page  408  /errors/408.html;
    error_page  500  /errors/500.html;
    error_page  503  /errors/503.html;

    # Make sure the paths to the custom error pages are not transformed
    # before sending the actual status codes to a request.
    # These error pages are for *internal* use only.
    location = "/errors/400.html" {
        internal;
    }
    location = "/errors/401.html" {
        internal;
    }
    location = "/errors/403.html" {
        internal;
    }
    location = "/errors/404.html" {
        internal;
    }
    location = "/errors/408.html" {
        internal;
    }
    location = "/errors/500.html" {
        internal;
    }
    location = "/errors/503.html" {
        internal;
    }

    # Remove "index" from URL if requesting the front page.
    location ~ "^/index(\.html)?$" {
        return  301  "/";
    }

    # Strip .html file extension, trailing slash, or index,
    # forcing the "pretty" URL, if user requests a ".html" URL.
    location ~ "^/(.+)(\.html|/|/index|/index.html)$" {
        return  301  "/$1";
    }

    [...]
}

location包含指令的块可防止internal错误页面受到常规 URL 处理的影响。我确实尝试过只匹配目录,/errors/这样就不必internal一遍又一遍地重复指令,但 Nginx 会将传入的请求与重写规则进行匹配,基本上是按规则选择器的特殊性进行排序:只有与每个自定义错误页面完全匹配的请求才具有足够的特殊性,才能在 Nginx 开始对错误页面进行 URL 重写处理之前抓住错误页面。

(我对 Nginx 术语的使用可能有点不对,但在我看来这就是正在发生的事情。并且这个配置完全按照代码注释所说的那样,这正是我从一开始就想要的。)

相关内容