Использование правил перезаписи Apache в .htaccess для удаления .html, вызывающего ошибку 500

Я написал небольшой веб-сайт (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.

Я проверил логи и не понял, почему это происходит, но забыл включить это в вопрос.

1
задан 25 October 2019 в 13:09
1 ответ

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 , если вам следует добавить больше правил, то, вероятно, так и будет.

1
ответ дан 3 December 2019 в 22:59

Теги

Похожие вопросы