私は小さな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.
私はログをチェックしましたが、なぜそれが起こっているのかわかりませんでしたが、これを質問に含めるのを忘れていました。
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になります。
これは正規表現ではないため、RewriteCond
TestStringのリテラルドットをバックスラッシュでエスケープする必要がないことに注意してください。
マイナーポイント... ^(.*)$
の_^
_アンカーと_$
_アンカーは、正規表現がデフォルトで貪欲であるため不要です(一部のユーザーは読みやすさ?)また、L
にlast
(RewriteRule
)フラグを含める必要があります。これが_.htaccess
_ファイルの唯一の(または最後の)ルールである場合、これは必要ありませんが、後でさらにルールを追加する必要がある場合は、おそらくそうです(そして、この方法で既存のルールを変更することを忘れないようにする必要がある傾向がありますエラー)。