Я написал небольшой веб-сайт (4 страницы, только HTML), и я хочу удалить расширение .html из URL-адреса, поместив некоторые правила перезаписи в свой .htaccess, я погуглил и нашел несколько фрагментов, похожих на этот:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}\.html -f
RewriteRule ^(.*)$ $1.html
</IfModule>
Оба следующих URL-адреса обслуживают один и тот же контент (чего я и ожидал)
https://example.io/contact
https://example.io/contact.html
Однако следующее дает ошибку 500:
https://example.io/contact/
Th Это каталог не существует, и если я удалю код перезаписи, упомянутый выше, вместо этого он будет 404, чего я ожидал. Почему приведенный выше код вызывает ошибку 500?
Еще более интересно то, что это будет 500:
https://example.io/contact/blah
Но это будет 404:
https://example.io/contact123/blah
Ни contact /, ни contact123 / не существует в качестве каталога, но contact.html существует и contact123.html - нет.
Любая помощь или объяснение будут оценены.
Изменить:
MrWhite уже дал правильный ответ, но для всех, кто ищет в будущем, журналы ошибок Apache выглядят следующим образом:
[Thu Oct 24 20:49:47.722210 2019] [core:error] [pid 13001:tid 139915446667008] [client 1.2.3.4:39006] AH00124: Request exceeded the limit of 10 internal redirects due to probable configuration error. Use 'LimitInternalRecursion' to increase the limit if necessary. Use 'LogLevel debug' to get a backtrace.
Я проверил логи и не понял, почему это происходит, но забыл включить это в вопрос.
tl; dr Запрос на / contact /
(или / contact / blah
) приводит к циклу перезаписи (500 внутренних серверов Ответ об ошибке), потому что REQUEST_FILENAME
содержит путь сопоставленной файловой системы; не тот URL-путь, который вы ожидаете.
RewriteCond% {REQUEST_FILENAME}! -d RewriteCond% {REQUEST_FILENAME} \. Html -f RewriteRule ^ (. *) $ 1.html
«Проблема» заключается в использовании REQUEST_FILENAME
во втором условии. Переменная сервера REQUEST_FILENAME
содержит абсолютный путь файловой системы после URL был сопоставлен с файловой системой. Это не обязательно то же самое, что и URL-путь, но это условие предполагает , что это так. Когда URL-путь содержит целые сегменты пути, которые не отображаются в файловой системе (как в / contact / blah
или / contact123 / blah
), то REQUEST_FILENAME
по существу «сводится» к последнему сегменту пути, который соответствует каталогу, плюс «имя файла» (т.е. ... / contact
и ... / contact123
соответственно - корень документа, то есть /
, является последним совпавшим каталогом в этом примере).
/ контакт
Когда вы запрашиваете / контакт
, тогда URL-путь - / contact
, а REQUEST_FILENAME
- / path / to / document-root / contact
, поэтому REQUEST_FILENAME
отображается непосредственно на URL -дорожка. Условие проверки /path/to/document-root/contact.html
выполнено успешно, и запрос перезаписывается на contact.html
. Все в порядке.
/ contact /
или / contact / blah
Однако, когда вы запрашиваете / contact /
, тогда URL-путь будет ] / contact /
, но REQUEST_FILENAME
снова будет / path / to / document-root / contact
(без суффикса косой черты). Условие проверки снова выполнено успешно (как указано выше), но запрос переписывается в contact / .html
(поскольку .html
добавляется к захваченному URL- путь, например $ 1.html
). Циклы обработки, REQUEST_FILENAME
оценивается так же, как и раньше (условие снова выполнено успешно), и запрос перезаписывается второй раз на contact / .html.html
. И т. Д. И т. Д., Что приводит к циклу перезаписи, который в конечном итоге достигает внутреннего предела (по умолчанию 10), когда он «ломается» и сервер отвечает внутренней ошибкой сервера 500.
/ contact123 / blah
/ contact123 / blah
, с другой стороны, приводит к 404, потому что серверная переменная REQUEST_FILENAME
становится / path / to / document-root / contact123
и / path /to/document-root/contact123.html
не существует, поэтому перезапись не происходит.
Чтобы «исправить» такое поведение, вы должны использовать REQUEST_URI
серверную переменную. Он содержит относительный к корню URL-путь. Добавьте это в серверную переменную DOCUMENT_ROOT
, чтобы создать имя файла для проверки.
Например:
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI}\.html -f
RewriteRule (.*) $1.html [L]
Теперь условие проверки проверяет тот же путь файловой системы, на который будет перезаписан запрос (если успешно).
Запрос / contact /
, / contact / blah
или / contact123 / blah
теперь приводит к 404, как и ожидалось.
ОБНОВЛЕНИЕ: Незначительные моменты ... якоря ^
и $
на ^ (. *) $
не нужны, поскольку регулярное выражение жадно по умолчанию (хотя некоторым они все же нравятся за читабельность ?). Вы также должны включить флаг L
( последний
) в RewriteRule
. Хотя в этом нет необходимости, если это единственное (или последнее) правило в файле .htaccess
, если вам следует добавить больше правил, то, вероятно, так и будет.