В сети можно найти достаточно много различных примеров с правилами реврайтов в .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>
