Назад | Перейти на главную страницу

Nginx - как отключить перезапись для error_page

В настоящее время я размещаю несколько небольших статических веб-сайтов с использованием Nginx 1.10.2 на Fedora 25 Server Edition x86_64.

Я настроил Nginx, чтобы .html для запросов без расширения файла (try_files) и перенаправить (постоянный rewrite) к .html-без версии 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/ {} блок (который я считать должно означать, что перезапись применяется только к URL-адресам, не в каталоге / errors /). Но все, что я сделал, привело к ошибке 404, постоянно перенаправляющей на 404 страница, который тогда не возвращает фактический статус 404 --- или он застревает в цикле перенаправления.

Я нашел более простое решение, вдохновленное ответом @ 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 может быть немного неуместным, но мне кажется, что именно это и происходит. И эта конфигурация делает именно то, что говорится в комментариях к коду, чего я хотел с самого начала.)

Это был крепкий орешек, но, наконец, это сработало, как и требовалось:

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

    [...]
}

Попробую дополнить это комментариями чуть позже;)

Я бы посоветовал вам обернуть 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/ {
    }
}