В настоящее время я размещаю несколько небольших статических веб-сайтов с использованием 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 делает:
/fake-page
/fake-page
, /fake-page.html
, или /fake-page/index.html
./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/ {
}
}