web-dev-qa-db-ja.com

.htaccessのApache書き換えルールを使用して.htmlを削除すると、500エラーが発生する

私は小さなWebサイト(4ページ、HTMLのみ)を作成し、.htaccessファイルにいくつかの書き換えルールを追加して、URLから.html拡張子を削除したいと考えています。

<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/

このディレクトリは存在せず、上記の書き換えコードを削除すると、代わりに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.

私はログをチェックしましたが、なぜそれが起こっているのかわかりませんでしたが、これを質問に含めるのを忘れていました。

2
Enicli

tl; dr _/contact/_(または_/contact/blah_)の要求は、再書き込みループ(500内部サーバーエラー応答)になります。これは、_REQUEST_FILENAME_にマップされたファイルシステムパスが含まれているためです。あなたが期待しているURLパスではありません。


_RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}\.html -f
RewriteRule ^(.*)$ $1.html
_

「問題」は、2番目の条件での_REQUEST_FILENAME_の使用です。 _REQUEST_FILENAME_サーバー変数には、URLがファイルシステムにマップされた後の絶対ファイルシステムパスが含まれます。これは必ずしもURLパスと同じではありませんが、この条件は同じであると想定しています。 URL-pathに、ファイルシステムにマップされないパスセグメント全体が含まれる場合(_/contact/blah_または_/contact123/blah_のように)、_REQUEST_FILENAME_は、マップする最後のパスセグメントに基本的に「縮小」されますディレクトリに「ファイル名」を加えたもの(つまり、それぞれ_.../contact_および_.../contact123_-ドキュメントルート、つまり_/_は、この例では最後に一致したディレクトリです)。

リクエスト_/contact_

_/contact_を要求すると、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_がcapturedURL-pathに追加されているため、つまり、_$1.html_)。ループの処理中、_REQUEST_FILENAME_は以前と同じように評価され(条件は再び成功します)、リクエストは2回目に_contact/.html.html_に書き換えられます。など、その結果、書き換えループが発生し、最終的に「ブレーク」してサーバーが500内部サーバーエラーで応答すると、内部制限(デフォルトは10)に達します。

リクエスト_/contact123/blah_

一方、_/contact123/blah_は、_REQUEST_FILENAME_サーバー変数が_/path/to/document-root/contact123_になり、_/path/to/document-root/contact123.html_が存在しないため404になります。そのため、そもそも書き換えは行われません。

"解決"

この動作を「修正」するには、代わりに_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になります。

これは正規表現ではないため、RewriteCondTestStringのリテラルドットをバックスラッシュでエスケープする必要がないことに注意してください。

マイナーポイント... ^(.*)$の_^_アンカーと_$_アンカーは、正規表現がデフォルトで貪欲であるため不要です(一部のユーザーは読みやすさ?)また、LlastRewriteRule)フラグを含める必要があります。これが_.htaccess_ファイルの唯一の(または最後の)ルールである場合、これは必要ありませんが、後でさらにルールを追加する必要がある場合は、おそらくそうです(そして、この方法で既存のルールを変更することを忘れないようにする必要がある傾向がありますエラー)。

2
MrWhite