Почему правило перенаправления abcd.http в abcd не работает без указания одного и того же регулярного выражения в RewriteCond и RewriteRule? - PullRequest
1 голос
/ 12 апреля 2020

У меня есть правило, которое сводится к следующему:

RewriteCond %{REQUEST_URI} ^(.+)\.html$
RewriteRule ^(.+)\.html$ $1 [R=302,L]

Это не будет работать без первой строки, даже если во второй строке есть точно такое же регулярное выражение. Насколько я понимаю, если в конце нет ". html", RewriteRule не будет ничего переписывать, так почему он не может работать без этого RewriteCond? Попытка доступа к example.com/test/abcd.html приводит к ошибке в журнале сервера:

[REWRITE] detected external loop redirection with target URL: /test/abcd, skip.

Вот весь файл .htaccess:

RewriteEngine On

# HTTPS everywhere and strip WWW
RewriteCond %{HTTPS} !=on
RewriteCond %{HTTP_HOST} ^www\.(.+) [NC]
RewriteRule ^ https://%1%{REQUEST_URI} [L,R=301]

# if example.com/xxx is not directory AND example.com/xxx.html file exists
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}\.html -f
# rewrite example.com/xxx to example.com/xxx.html
# only if there's no slash at the end
RewriteRule ^(.*[^/])$ $1.html

# if example.com/xxx/ is not directory, rewrite to example.com/xxx
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)/$ $1 [R=301,L]

# if xxx.html is not directory AND xxx.html file exists
# redirect from xxx.html to xxx
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} -f
# won't work without line below, even though both have ^(.+)\.html$ - can't understand why
RewriteCond %{REQUEST_URI} ^(.+)\.html$
RewriteRule ^(.+)\.html$ $1 [R=301,L]

Ответы [ 2 ]

1 голос
/ 13 апреля 2020

Ваши правила генерируют бесконечное перенаправление l oop. Действительно, что-то вроде foo/bar.html переходит к foo/bar, что внутренне go до foo/bar.html, go обратно к foo/bar и так далее.

Следующие правила предотвратят такое перенаправление l oop (с некоторыми улучшениями):

RewriteEngine On

# strip www
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
RewriteRule ^ https://%1%{REQUEST_URI} [L,R=301]

# HTTPS everywhere
RewriteCond %{HTTPS} !=on
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

# if example.com/xxx/ is not directory, rewrite to example.com/xxx
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.+)/$ /$1 [R=301,L]

# if xxx.html is not directory AND xxx.html file exists
# redirect from xxx.html to xxx
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} -f
RewriteCond %{THE_REQUEST} \s/(.+)\.html(?:\s|\?) [NC]
RewriteRule ^ /%1? [R=301,L]

# if example.com/xxx is not directory AND example.com/xxx.html file exists
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}\.html -f
# rewrite example.com/xxx to example.com/xxx.html
RewriteRule ^(.+)$ /$1.html [L]
0 голосов
/ 14 апреля 2020

Я наконец понял, почему это не сработало.

В исходной версии структура файла выглядела следующим образом:

  1. перенаправить на HTTPS и не WWW (и прекратить обработку последующих правил - флаг [L])
  2. перенаправить /foo/bar.html/ на /foo/bar.html (и остановить, как указано выше)
  3. переписать /foo/bar на /foo/bar.html (внутренне)
  4. перенаправить /foo/bar.html на /foo/bar (и остановиться, как в 1. и 2.)

Итак, когда запрашивалось /foo/bar.html, оно соответствовало правилу 4. и перенаправлялось на /foo/bar. Затем перезапись была начата снова, так как был сделан новый запрос к /foo/bar, и он был переписан как /foo/bar.html (3.). Затем он перешел к следующему правилу - 4. (снова) - и был перенаправлен обратно на /foo/bar, поэтому был сделан еще один запрос, и перезапись началась снова, но затем он был заблокирован сервером из-за циклов.

Есть два способа это исправить. Первый способ - изменить порядок двух последних операций:

  1. перенаправление на HTTPS и не WWW (и прекратить обработку последующих правил - флаг [L])
  2. redirect /foo/bar.html/ до /foo/bar.html (и остановка)
  3. перенаправление /foo/bar.html на /foo/bar (и остановка)
  4. перезапись /foo/bar на /foo/bar.html (внутренне)

В этом случае запрос на /foo/bar.html будет перенаправлен на /foo/bar (3.), как и раньше, а в новом запросе он будет перезаписан как /foo/bar.html внутри (4.), и все. Он не будет перенаправлен обратно на /foo/bar, потому что после 4. нет перенаправлений или других правил.

Второй способ - добавить флаг [L] к правилу, переписав /foo/bar в /foo/bar.html что даст тот же эффект, что и изменение порядка. Перезапись будет go как:

  1. перенаправление на HTTPS и не WWW (и остановка)
  2. перенаправление /foo/bar.html/ на /foo/bar.html (и остановка)
  3. переписать /foo/bar в /foo/bar.html (внутренне) (и остановить)
  4. перенаправить /foo/bar.html в /foo/bar (и остановить)

Я go с первым способом (переупорядочением), поскольку это позволит мне добавить другие правила после правила "/ foo / bar в /foo/bar.html".

Финал (как сейчас ... ) .htaccess файл:

RewriteEngine On

# force HTTPS everywhere and strip WWW
RewriteCond %{HTTPS} !=on
RewriteCond %{HTTP_HOST} ^www\.(.+) [NC]
RewriteRule ^ https://%1%{REQUEST_URI} [R=301,L]

# rewrite example.com/xxx/ to example.com/xxx
# if example.com/xxx/ is not directory
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.+)/$ $1 [R=301,L]

# redirect from xxx.html to xxx
# if xxx.html is not directory AND xxx.html file exists
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule ^(.+)\.html$ $1 [R=301,L]

# rewrite example.com/xxx to example.com/xxx.html
# if example.com/xxx is not directory AND example.com/xxx.html file exists
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}\.html -f
RewriteRule ^ %{REQUEST_FILENAME}.html
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...