.htaccess mod_rewrite – Как перенаправить «/page.html» и «/page/» на «/page»?

Мне нужна помощь с моим файлом .htaccess. Я возился с ним около недели, но не могу заставить его работать.. Я искал в Интернете решения, но ничего не подходило для моего случая.

В общем, при вводе: website.com/info.html?page=12 должно появиться: website.com/info/12 и наоборот: website.com/info/12 должен служить: website.com/info.html?page=12.

Аналогично, без части QUERY_STRING: website.com/info.html должно показывать: website.com/info и website.com/info должен служить: website.com/info.html.

Однако! website.com/info/ (только если за ним ничего не следует) должно быть перенаправлено на: website.com/info.

Как я могу этого добиться? Это должно быть просто, но я пока не могу уложиться в этом.. Любые советы очень ценятся!

ОБНОВЛЯТЬ:

Спасибо за ответы и комментарии. Это, конечно, пролило некоторый свет на эту тему. Я почти на месте...

Начнем с того, что если у кого-то возникнут проблемы с пониманием регулярных выражений, эти инструменты мне очень помогли:

https://regexr.com

https://regex101.com

И аналогичный для тестирования файлов .htaccess:

https://htaccess.madewithlove.com

Некоторые уточнения:

Мне нужно общее .html скрытие для всех страниц, но только одно конкретное перенаправление с /info/ (каталога с таким именем нет) на /info.

Я прочитал параметр ?page=12 с помощью JS window.location.search, но не думаю, что это имеет значение, поскольку я могу воспроизвести ошибку на странице и без него.

Теперь мой файл .htaccess выглядит так:

RewriteEngine on

# Externally redirect: "/info.html?page=YYY" to: "/info/YYY" - WORKS
RewriteCond %{QUERY_STRING} page=(\d+) [NC]
RewriteRule ^info\.html$ /info/%1? [NC,R=302,NE,L]

# Externally redirect: "/XXX.html" to: "/XXX" - WORKS
RewriteCond %{THE_REQUEST} /(.+)\.html [NC]
RewriteRule ^ /%1 [NC,R=302,NE,L]

# Externally redirect: "/info/" to: "/info" - WORKS
RewriteRule ^info/$ /info [NC,R=302,L]

# Internally rewrite: "/info/YYY" to: "/info.html?page=YYY" - INTERNAL SERVER ERROR 
RewriteRule ^info/(\d+) /info.html?page=$1 [L]

# Internally rewrite: "/XXX" to: "/XXX.html" - WORKS
RewriteCond %{REQUEST_FILENAME}.html -f [NC]
RewriteRule ^ %{REQUEST_URI}.html [NC,L]

Как и написано в комментариях, все работают кроме 4-го.

Однако, если я удалю 1-й, 4-й заработает.

Что мне не хватает??

«Я возился с этим около недели» — Итак, что ты пробовал? И что происходит? Есть ли у вас какие-либо другие директивы в вашем .htaccess файле? И я предполагаю, что /info не сопоставляется с физическим каталогом? (Может быть, есть что-то более фундаментальное, что мешает этому работать, или просто небольшая поправка к существующим правилам.) Это только для info или вам нужно что-то более общее?

MrWhite 31.05.2024 18:13
/info.html?page=12 - Делаем шаг назад на минутку... как вы на самом деле читаете параметр URL?
MrWhite 01.06.2024 02:58
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
2
83
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Я бы начал с перенаправления всех расширений .html, чтобы они не отображались в URL.

Затем, если после него есть номер, вы можете перенаправить страницу = номер

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)\.html$ /$1 [L,R=301] 

#redirect /number to ?page=number
RewriteRule ^info/([0-9]+)$ /info?page=$1 [L]

Условия первого правила предотвратят перенаправление, поскольку /info.html — это физический файл. Вам не хватает .html в строке замены во втором правиле. Но в противном случае это приведет к циклу перенаправления. А как насчет запросов на /info.html?page=12 и /info/?

MrWhite 31.05.2024 18:16

Вы пишете: "website.com/info.html?page=12 должно показать: website.com/info/12...

На самом деле это не имеет особого смысла, если рассматривать переписывание URL-адресов. Конечно, вы можете перенаправить такой URL-адрес, но при этом все равно будет отображаться первый URL-адрес до того, как произойдет перенаправление. Нужно понимать, что рерайтинг работает на входящих запросах. Это никак не меняет исходящие ответы. Инструменты для этого существуют, но я сомневаюсь, что это имеет смысл в вашем случае. Вместо этого вам следует позаботиться о том, чтобы логика вашего приложения в первую очередь выдавала не этот более длинный URL-адрес, а тот, который вы действительно хотите использовать.

Перенаправление запросов с /info/ на /info, безусловно, возможно, хотя было бы тривиально просто пропустить перенаправление и напрямую доставить ожидаемый ответ. Как вам нравится. Обратите внимание, что это также может конфликтовать с другими настройками вашей настройки, о которых я ничего не знаю. Так что вам придется немного поэкспериментировать с этим.

В любом случае, это будет отправной точкой для внешнего перенаправления такого URL-адреса по запросу. И чтобы внутренне переписать входящий запрос на этот более короткий URL-адрес по желанию:

RewriteEngine on

# externally redirect https://example.com/info.html?page=12
RewriteCond %{QUERY_STRING} ^page=12$
RewriteRule ^/?info\.html$ /info/12 [R=301,L,QSD]

# externally redirect https://example.com/info.html
RewriteCond %{QUERY_STRING} ^$
RewriteRule ^/?info\.html$ /info [R=301,L]

# optional: externally redirect https://example.com/info/
RewriteRule ^/?info/$ /info [R=301,L]

# internally rewrite requests to https://example.com/info/12
RewriteRule ^/?info/12/?$ /info.html?page=12 [L]
# internally rewrite requests to https://example.com/info
RewriteRule ^/?info/?$ /info.html [L]

Очевидно, вы можете реализовать это более общим способом:

RewriteEngine on

# externally redirect https://example.com/info.html?page=<any number>
RewriteCond %{QUERY_STRING} ^page=(\d+)$
RewriteRule ^/?info\.html$ /info/%1 [R=301,L,QSD]

# externally redirect https://example.com/info.html
RewriteCond %{QUERY_STRING} ^$
RewriteRule ^/?info\.html$ /info [R=301,L]

# optional: externally redirect https://example.com/info/
RewriteRule ^/?info/$ /info [R=301,L]

# internally rewrite requests to https://example.com/info/<any number>
RewriteRule ^/?info/(\d+)/?$ /info.html?page=$1 [L]
# internally rewrite requests to https://example.com/info
RewriteRule ^/?info/?$ /info.html [L]

Вы можете реализовать эти правила в конфигурации хоста центрального http-сервера. Это предпочтительное место для таких правил по разным причинам. Если у вас нет к нему доступа (читай: если вы используете дешевого провайдера веб-пространства), вы можете вместо этого использовать распределенный файл конфигурации (обычно называемый «.htaccess»). Для этого вам необходимо позаботиться о том, чтобы такой файл находился в папке, настроенной как DocumentRoot внутри вашего http-хоста, чтобы файл был доступен для чтения процессом http-сервера и чтобы вы включили интерпретацию таких правил в таких файлах с помощью AllowOverride директива (подробнее об этом см. в документации).

Ответ принят как подходящий
# Internally rewrite: "/info/YYY" to: "/info.html?page=YYY" - INTERNAL SERVER ERROR 
RewriteRule ^info/(\d+) /info.html?page=$1 [L]

Я прочитал параметр ?page=12 с помощью JS window.location.search, но не думаю, что это имеет значение

Да, это важно. Вы не можете внутренне переписать (на сервере), чтобы добавить строку запроса и ожидать, что JS на стороне клиента сможет увидеть эту строку запроса. «Внутренняя перезапись» полностью выполняется внутри сервера. JS видит только ответ, отправленный обратно с сервера, который не включает строку запроса (если только она не присутствовала в исходном запросе или вы не перенаправили на него извне).

Вместо этого вы можете внутренне переписать запрос только для /info/12 на /info.html (без строки запроса) и использовать window.location.pathname для анализа URL-пути (т. е. /info/12) для извлечения страницы из второго сегмента пути (если таковой имеется).

Что касается вашей "ошибки". Первое правило перенаправляет вновь переписанный URL-адрес во время второго прохода механизма перезаписи, что приведет к возникновению цикла перенаправления. Последнее правило также может привести к внутреннему циклу перезаписи (внутренней ошибке сервера) при определенных условиях (поскольку условие с использованием REQUEST_FILENAME не проверяет то же самое, что и REQUEST_URI). Вам также необходимо убедиться, что MultiViews отключен в этом сценарии, так как в противном случае это будет конфликтовать с вашими перезаписями.

По какой-то причине вы прибегли к использованию пустой строки запроса (в конце ?), чтобы отбросить строку запроса в первом правиле. Если вы используете Apache 2.4 (как я предполагаю), то вместо этого вам следует использовать флаг QSD (отмена строки запроса), как указано в ответе @arkascha. Хотя ваше ошибочное использование флага NC при условии проверки на -f заставляет меня усомниться в вашей версии Apache.

Я предполагаю, что вы уже ссылаетесь на канонический URL-адрес внутри своего приложения, например. /info/12 (не /info.html?page=12). И эти перенаправления предназначены только для SEO (при изменении существующей структуры URL-адресов) или для пользователей, которые могут ввести старые (неканонические) URL-адреса.

Например, попробуйте вместо этого следующее:

# Ensure that MultiViews is disabled
Options -MultiViews

RewriteEngine on

# Externally redirect: "/info.html?page=<num>" to: "/info/<num>"
RewriteCond %{QUERY_STRING} ^page=(\d+)$ [NC]
RewriteRule ^info\.html$ /info/%1 [NC,QSD,R=302,L]

# Externally redirect: "/XXX.html" to: "/XXX"
RewriteRule ^(.+)\.html$ /$1 [NC,R=302,L]

# Externally redirect: "/info/" to: "/info"
RewriteRule ^info/$ /info [NC,R=302,L]

# Internally rewrite: "/info/<num>" to: "/info.html"
RewriteRule ^info/\d+$ info.html [END]

# Internally rewrite: "/XXX" to: "/XXX.html" if it exists
RewriteCond %{DOCUMENT_ROOT}/$1.html -f
RewriteRule (.+) $1.html [END]

Я использовал END (требуется Apache 2.4) в последних двух правилах, чтобы предотвратить второй цикл механизма перезаписи, который в противном случае привел бы к циклу перенаправления.

Также обратите внимание на добавление привязки конца строки ($) к исходному регулярному выражению ^info/(\d+), в противном случае вы разрешаете URL-адреса формы /info/12<anything>.

Я удалил префикс косой черты в строках замены двух перезаписей, поскольку мы переписываем путь к файлу, а не путь к URL-адресу. Переписывая URL-путь (префикс косой черты), вы (без необходимости) заставляете механизм перезаписи пересопоставлять URL-адрес с файловой системой.

Вы должны быть осторожны при проверке THE_REQUEST, поскольку сюда также входит строка запроса (если таковая имеется), которую вам следует избегать при проверке .html.

И, как уже упоминалось, вам нужно будет использовать window.location.pathname в JS на стороне клиента вместо этого для анализа номера страницы (если есть) из запрошенного URL-адреса.

Обратите внимание, что это можно дополнительно оптимизировать/упростить, если мы узнаем больше о вашей системе/структуре URL-адресов. Например, вы имеете дело только с URL-адресами в корне документа, как в вашем примере, или вы также ожидаете /foo/bar/baz? Содержат ли ваши URL-адреса точки? (В противном случае это привело бы к разделению расширений файлов на ваших статических ресурсах.)

Другие вопросы по теме