В сети можно найти достаточно много различных примеров с правилами реврайтов в .htaccess , добрая половина из которых содержит ошибки, множественные перенаправления(что отрицательно скажется на seo) и прочую гадость. Скажу по секрету: вторая половина вам тоже не нужна! Краткость — сестра таланта. Это аксиома.
Отойдём от этой аксиомы, для ввода в курс дела. Если пришел за правилами, гоу сразу вниз статьи.
Слишком много нюансов и правил в htaccess, на разных серверах поведут себя неадекватно, что-то можно упустить и прощай оптимизация и первые позиции в пс. Страждущих можно наблюдать на фрилансах с вопросами: «Помагите, сайту годы, страниц охулиард, в консоли проиндексировано пару сотен, где наше всё?»
Я бы рекомендовал таким писакам правил, командную строку + curl в зубы с параметрами отладки — заголовков и смотреть глазками, сколько переадресаций, каких, когда, куда, при каких обстоятельствах и т.п. А ещё удобнее для отладки, использовать менеджер запросов Postman, просто, красиво, всё на ладони:
Откроем файл .htaccess любого wp движка, как известно wordpress поддерживает различные схемы формирования url на выбор.
<ifModule mod_headers.c> <FilesMatch ".(flv|gif|jpg|jpeg|png|ico|swf|js|css|pdf)$"> Header set Cache-Control "max-age=2592000" </FilesMatch> </IfModule> # BEGIN WordPress # Директивы (строки) между `BEGIN WordPress` и `END WordPress` # созданы автоматически и подлежат изменению только через фильтры WordPress. # Сделанные вручную изменения между этими маркерами будут перезаписаны. <IfModule mod_rewrite.c> RewriteEngine On RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] RewriteBase / RewriteRule ^index\.php$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . /index.php [L] </IfModule> # END WordPress
Мы увидим весьма лаконичные правила. Все идёт на index.php а дальше PHP логика рулит. Отделяем далее основной REQUEST_URI от параметров GET просто:
$request = strtok($_SERVER['REQUEST_URI'], '?');
Мухи отдельно, котлеты отдельно. И рулим уже кодом! Это правильно.
Поговорим о редиректах 301 убирающих/добавляющих слэши в конце(касается в общем-то любых редиректов)
RewriteCond %{REQUEST_URI} (.+)/$ RewriteRule .* %1 [R=301,L]
На какой протокол apache вернет 301? Будучи на 80 порту он вернет редирект на http, а на 443 — https
Итак, у вас нет своего сертификата на сервере, он вам не нужен. Вы за flexible от cloudflare юзаете его https, при разработке вы протестили свои htaccess редиректы, на 80 порту apache у вас запущен, всё окей? Нет.Хотя работает и за клоудом. Но как? Через жопу:
- Получая запрос по протоколу https(443) на /request///// клоуд отправляет его на сервер в том же виде на протокол http(80)
- Получая запрос /request///// apache редиректит его на по своему 80 протоколу http://example.com/request/
- Получая запрос по протоколу http(80) на /request/ клоуд редиректит его на https (вы же поставили always use https?)
- Получая запрос по https на /request/ клоуд наконец отправляет его в том же виде на сервер по протоколу 80
Результат — 1 лишний редирект. А вот так правильно:
RewriteCond %{REQUEST_URI} (.+)/$ RewriteRule .* https://%{SERVER_NAME}%1 [R=301,L]
Это же относится ко всем вариантам редиректов 301-302. И вот таких подводных камней может быть масса, особенно когда начинают извращаться, разбирая по слешам запросы в .htaccess, формируя кучу реврайтов, передавая из «разобранного» между слешей на GET параметры всякие категории, id по отдельности и т.п. Зачем? Юзай себе один реврайт всегда на index.php, из него разбирай запрос как удобно, GET параметры и подключай логику инклудами, php гибок и прост! Кроме того, если придется перебираться на nginx — меньше будет мороки с трансформированием в его правила.
Итак, пару золотых правил .htaccess
- Без слешей в конце url
- Со слешами в конце url
В обоих случаях двойные слеши будут удалены редиректом, а также будут удалены index.php/index.html в конце любого пути
Без слешей:
DirectoryIndex index.php AddDefaultCharset UTF-8 Options -Indexes <ifModule mod_headers.c> <FilesMatch ".(flv|gif|jpg|jpeg|png|ico|swf|js|css|pdf)$"> Header set Cache-Control "max-age=2592000" </FilesMatch> </IfModule> <IfModule mod_rewrite.c> RewriteEngine On RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] RewriteBase / RewriteCond %{REQUEST_URI} (.+)/$ RewriteRule .* https://%{SERVER_NAME}%1 [R=301,L] RewriteCond %{THE_REQUEST} // RewriteRule .* https://%{SERVER_NAME}/$0 [R=301,L] RewriteCond %{REQUEST_URI} ^(.+)/index\.(php|html) RewriteRule ^(.+)/index\.(php|html)([^/]*)$ https://%{SERVER_NAME}/$1 [R=301,L] RewriteCond %{THE_REQUEST} /index\.(php|html) RewriteRule ^index\.(php|html)([^/]*)$ https://%{SERVER_NAME}/ [R=301,L] RewriteRule ^index\.php$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteRule . /index.php [L] </IfModule>
И со слешами:
DirectoryIndex index.php AddDefaultCharset UTF-8 Options -Indexes <ifModule mod_headers.c> <FilesMatch ".(flv|gif|jpg|jpeg|png|ico|swf|js|css|pdf)$"> Header set Cache-Control "max-age=2592000" </FilesMatch> </IfModule> <IfModule mod_rewrite.c> RewriteEngine On RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] RewriteBase / RewriteCond %{THE_REQUEST} // RewriteRule .* https://%{SERVER_NAME}/$0 [R=301,L] RewriteCond %{REQUEST_URI} ^(.+)/index\.(php|html) RewriteRule ^(.+/)index\.(php|html)([^/]*)$ https://%{SERVER_NAME}/$1 [R=301,L] RewriteCond %{THE_REQUEST} /index\.(php|html) RewriteRule ^index\.(php|html)([^/]*)$ https://%{SERVER_NAME}/ [R=301,L] RewriteCond %{REQUEST_URI} !(.*)/$ RewriteCond %{REQUEST_URI} !(\.html|\.txt|\.xml)$ RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^(.*[^/])$ https://%{SERVER_NAME}/$1/ [L,R=301] RewriteRule ^index\.php$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteRule . /index.php [L] </IfModule>