web-dev-qa-db-ja.com

$ .htmlを$ / mod_rewriteルールの衝突にリダイレクトします

たとえば、/ sub/test /からtest.htmlにURLをリマップしましたが、/ sub/test.htmlを/ sub/test /にリダイレクトしたいのですが、ここでルールが衝突しているようです。

.htaccessファイルは/ subディレクトリにあり、/ subディレクトリとその下のサブディレクトリにのみ適用する必要があります。

.htaccessファイルは次のとおりです。

<IfModule mod_rewrite.c>
Options -MultiViews -Indexes
RewriteEngine On
RewriteBase /sub

# redirect urls without trailing slash
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !index.html
RewriteCond %{REQUEST_URI} !(.*)/$
RewriteRule (.*)$ $1/ [L,R=301]

# redirect .html to url
#RewriteCond %{REQUEST_FILENAME} !-d
#RewriteCond %{REQUEST_FILENAME} -f
#RewriteRule (.*)\.html$ $1/ [L,R=301]

# remap url to a .html file
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule (.*)/$ $1.html [L]

</IfModule>

ロジックは、/ sub/test.htmlパスが/ sub/test /にリダイレクトされ、内部で/sub/test.htmlに再マッピングされることです。

.htaccessファイルは/ subディレクトリにあり、/ subディレクトリとその下のサブディレクトリにのみ適用する必要があります。

コメント行は、目的のURLである/ sub/test /へのリダイレクトループを引き起こします。コメントされた行だけが残っている場合、リダイレクトループはないため、ルール間に矛盾があるようです。リダイレクトループの原因は何ですか?

存在しないファイルリダイレクトループ:

存在しないファイルリダイレクトループに対処する方法たとえば、/ web.htmlは/ web /にリダイレクトされますが、存在しない/web2.htmlは/web.html.html.htmlのようになります...

3
DominicM

リダイレクトループの原因は何ですか?

「最後のディレクティブ」が実行されると、書き換えプロセスは突然完全に停止するのではなく、現在のpassのみが停止します。書き換えプロセス全体が再び上から始まります!このプロセスは、URLが変更されずに通過した場合(またはApache 2.4でENDフラグに達した場合にのみ完全に停止します(Ivo van der Veekenが答えで言及しているように)。

「最後のディレクティブ」は、ファイル内の最後のディレクティブ、またはLlast)フラグで処理されるディレクティブのいずれかです。

RewriteRule (.*)/$ $1.html [L]

したがって、上記のディレクティブが実行されると、書き換えプロセスが再び開始されますが、今回は、上記の(コメント化された)ディレクティブによってキャッチされ、.html(追加されたばかり)を削除してリダイレクトします。次の要求で、.htmlが再び追加されます(内部書き換え用)が、書き換えプロセスが再び開始され、.htmlが削除され、リダイレクトなどが行われます。

ループを解除するには、上記のENDの代わりにLフラグ(Apache 2.4+)を使用できます。または、.htmlに対してチェックする追加の条件(RewriteCondディレクティブ)をTHE_REQUESTredirectで使用します。このサーバー変数は、rewrittenリクエストではなく、最初のリクエスト(クライアントから送信された実際のリクエストヘッダー)の値を保持するため、リクエストが書き換えられたときに一致しません「ループ」を破る。これは、Apacheのすべてのバージョンで機能します。だから、次のようなものを試してください:

# redirect .html to url
RewriteCond %{THE_REQUEST} ^[A-Z]+\ /.+\.html\ HTTP/
#RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule (.+)\.html$ $1/ [L,R=301]

上記の条件により、.htmlが書き換えられたリクエストではなく、最初のリクエストにのみ存在することが保証されます。 <something>.html?という名前のディレクトリがない限り、それがディレクトリではないことを本当に確認する必要はないと思います。

THE_REQUESTは次のようになります。

GET /sub/test.html HTTP/1.1

存在しないファイルリダイレクトループ:

# remap url to a .html file
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule (.*)/$ $1.html [L]

書き換えディレクティブは、要求されたファイルが存在しない場合は常に、要求に.htmlを追加します。したがって、does-not-exist/does-not-exist.htmlに書き換えられ、does-not-exist.html/に外部からリダイレクトされます(最初のルールによって)does-not-exist.html.htmlなどに書き換えられます。

追加のチェックを含めて、実際に書き換える前に、書き換えられたファイルが存在することを確認できます。例えば。 RewriteCond %{DOCUMENT_ROOT}/sub/$1.html -f-追加の複雑さは、URLがスラッシュで終わるためです。コンテキスト内:

# remap url to a .html file
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{DOCUMENT_ROOT}/sub/$1.html -f
RewriteRule (.*)/$ $1.html [L]

編集:これをさらにgeneric/subサブディレクトリを含める必要を避けるため)にするには、3番目の条件を次のように変更してみてください。

RewriteCond %{REQUEST_FILENAME}.html -f

これは、リクエストから末尾のスラッシュを内部的に除去するApacheに依存しています。

rewriteBaseを動的にすることにより、移植性を高めます

この.htaccessファイルがRewriteBaseディレクティブ(このインスタンスにある)に指定しているのと同じディレクトリにある場合、サブディレクトリを動的に割り当てることができます環境変数に追加し、RewriteRule置換で使用します。

# Removed...
#RewriteBase /sub

# Assign the accessed subdirectory to an environment variable
RewriteCond %{REQUEST_URI} ^(/[^/]+)/
RewriteRule ^ - [E=SUBDIR:%1]

# Use this environment variable as a prefix to your substitutions
:
RewriteRule (.*)$ %{ENV:SUBDIR}/$1/ [L,R=301]

ちなみに、これは外部リダイレクトにのみ必要です。ディレクトリごとの.htaccessファイルのinternal rewritesには、ディレクトリプレフィックスが自動的に相対置換に追加されるため、これは必要ありません。

これは、/ subディレクトリが任意の名前のディレクトリになる可能性があることを意味しますまたは複数のサブディレクトリ

複数のサブディレクトリの場合、次のようなものでCondPatternを変更できます。

RewriteCond %{REQUEST_URI} ^(/.+)/

デフォルトでは、正規表現はgreedyであるため、最初と最後のスラッシュの間のすべてがキャプチャされます。ただし、これにより一部のURL /サーバーが破損する可能性があり、XSS攻撃にさらされる可能性があるため、汎用的ではなく、特定の要件に合わせてコーディングします。

1
MrWhite

まず、RewriteCond %{REQUEST_FILENAME} !index.htmlは機能しません。引用符付きの!"index\.html"が必要です。それ以外の場合、有効な正規表現ではありません。

docs によれば、[END]の代わりに[L]フラグを使用して、さらなるリクエスト処理を防止する必要があります(内部的には、書き換えはApacheリダイレクトルールによって再評価されます)。どうやら バグ with [END]がありますが、これは2.4.9でのみ修正されているため、別の方法を見つける必要があるかもしれません。

また、ステートメントの順序を切り替えます。ほとんどのリンクはクリーンなURLを指している必要があるため、これは少し速くなるはずです。

# remap url to a .html file
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule (.*)/$ $1.html [END]

# redirect .html to url
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule (.*)\.html$ $1/ [L,R=301]
2

ほぼ完全に機能する最後の.htaccessを次に示します。

<IfModule mod_rewrite.c>
Options -MultiViews -Indexes
RewriteEngine On
#RewriteBase /sub

# Assign the accessed subdirectory to an environment variable
RewriteCond %{REQUEST_URI} ^(/[^/]+)/
RewriteRule ^ - [E=SUBDIR:%1]

ErrorDocument 404 /%{ENV:SUBDIR}/404.html

# redirect urls without trailing slash
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !index\.html
RewriteCond %{REQUEST_URI} !(.*)/$
RewriteRule (.*)$ %{ENV:SUBDIR}/$1/ [L,R=301]

# redirect index.html to sub root
RewriteCond %{THE_REQUEST} index\.html
RewriteRule ^index.html$ /%{ENV:SUBDIR}/ [L,R=301]

# redirect .html to url
RewriteCond %{THE_REQUEST} (.*)\ /%{ENV:SUBDIR}/.+\.html
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule (.*)\.html$ $1/ [L,R=301]

# remap url to a .html file
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{DOCUMENT_ROOT}/%{ENV:SUBDIR}/$1.html -f
RewriteRule (.*)/$ $1.html [L]

</IfModule>

ニースになる唯一の変更は、RewriteBaseを動的にすることで移植性を高めることです。 RewriteBaseを設定する代わりに、/ subディレクトリを動的に取得して使用する方法があると確信しています。つまり、/ subディレクトリは、任意の名前のディレクトリ、または複数のサブディレクトリにさえなる可能性があります。

0
DominicM