Полное руководство по mod_rewrite (часть 5): Частые случаи и примеры использования mod_rewrite


Оглавление. Полное руководство по mod_rewrite

1. Как включить и как работает mod_rewrite

2. Регулярные выражения mod_rewrite

3. Флаги RewriteRule

4. Директива RewriteCond

5. Частые случаи и примеры использования mod_rewrite

5.1 Проверка доступности mod_rewrite

5.1.1 Как включить RewriteEngine

5.1.2 Как проверить, включён ли mod_rewrite

5.1.3 Как сделать так, чтобы правила перезаписи использовались только если mod_rewrite включен

5.2 Использование mod_rewrite для перенаправления (редиректа) и переназначения URL

5.2.1 Страница поменяла адрес, как показать новую страницу по старому адресу без редиректа

5.2.2 Страница поменяла адрес, как перенаправить на новую страницу при запросе старой (редирект)

5.2.3 Переадресация при смене домена

5.2.4 Простой редирект на новый сайт

5.2.5 Как переправить все запросы из одной директории, в другую

5.2.6 Использовать URL адресов без расширения файлов .php

5.2.7 Универсальный документ ошибки (Error Document) для не найденных ресурсов (ошибка 404 Not Found)

5.2.8 Со статики на динамику

5.2.9 Обратная совместимость для изменений расширения файла

5.2.10 Замена на WebP изображения


5.3 Канонические имена хостов и URL. HTTPS

5.3.1 Как сделать редирект с HTTP на HTTPS

5.3.2 Как сделать редирект на с HTTP на HTTPS всех страниц кроме некоторых

5.3.3 Как сделать редирект на с HTTP на HTTPS только некоторых страниц

5.3.4 Принудительное использование HTTPS за прокси

5.3.5 Всегда использовать WWW перед именем домена

5.3.6 Всегда использовать WWW перед именем домена – универсальный вариант

5.3.7 Никогда не использовать WWW перед именем домена

5.3.8 Никогда не использовать WWW перед именем домена – универсальный вариант

5.3.9 Принудительное использование канонического имени с HTTPS и www

5.3.10 Канонический вид с HTTPS и без www

5.3.11 Принудительное SSL и www для главного домена, принудительное SSL без www для всех поддоменов (кроме локальных)

5.3.12 Принудительное добавление конечного слеша к адресу сайта

5.3.13 Удаление конечного слеша

5.3.14 Удаление лишних слешей в адресе URL


5.4 Контроль доступа и блокировка хотлинка

5.4.1 Ограничение доступа по IP

5.4.2 Запрет доступа к скрытым файлам и директориям

5.4.3 Запрет хотлинка изображений

5.4.4 Запрет хотлинкинга только для определённых доменов

5.4.5 Разрешение хотлинка для определённых сайтов

5.4.6 Блокировка пользователя по рефереру (Referrer)

5.4.7 Блокировка плохих ботов, клонеров сайтов, офлайн браузеров

5.4.8 Запрет доступа к файлу или директории в определённые периоды времени

5.4.9 Запрет доступа с пустым реферером (Referer)

5.4.10 Блокировка доступа по части пользовательского агента (User Agent)

5.4.11 Как заблокировать доступ по точному совпадению User Agent

5.4.12 Запрет доступа к определённым страницам

5.4.13 Запрет доступа IP и диапазонам

5.4.14 Комбинирование правил контроля доступа


5.4.15 Как фильтровать по строке запроса, идущей после знака вопроса

5.5 Обработка строки параметров URI в mod_rewrite

5.5.1 Запросы: request и query

5.5.2 Как в mod_rewrite искать по параметрам после знака вопроса

5.5.3 Как с mod_rewrite удалить параметр из URI

5.5.4 Как удалить часть строки запроса после знака вопроса

5.5.5 mod_rewrite: отрицание в строке запроса %{QUERY_STRING}

5.5.6 Как в mod_rewrite переписать строку запроса

5.5.7 Ошибка ERR_TOO_MANY_REDIRECTS при удалении строки запроса. Как использовать флаг QSD для удаления строки запроса

5.6 Перенаправление портов

5.6.1 Как перенаправить запросы на 80-й порт

5.6.2 Как перенаправить запросы на 443-й порт

5.6.3 Как перенаправить запросы на 80-й порт кроме некоторых страниц

5.7 Как в mod_rewrite читать параметры POST

6. Продвинутые техники

7. Директива RewriteMap

8. Директива RewriteOptions, технические подробности, когда НЕ использовать mod_rewrite


В предыдущих частях мы изучили практически всю документацию по mod_rewrite. Остались директивы RewriteMap и RewriteOptions. RewriteMap также используется для перезаписи URL адресов, но применяется реже других; к ней мы вернёмся позже. Директива RewriteOptions также применяется нечасто. Особенностью RewriteMap является то, что её нельзя использовать в .htaccess. Её можно использовать только в контексте сервера, либо виртуальных хостов. По большому счёту, RewriteMap не добавляет новой функциональности – она только позволяет вынести большой массив данных, которые нецелесообразно или слишком сложно описывать при помощи регулярных выражений, в отдельные файлы. Получаются такие выделенные базы данных. Тем не менее, мы всё равно рассмотрим RewriteMap в одной из последующих частей.

Сейчас для закрепления изученной теории, мы перейдём к практическим примерам самых частых случаев использования mod_rewrite, включая подробное описание того, как они работаю. Если после знакомства с теорией и этими примерами у вас остались вопросы, то пишите их здесь в комментариях.

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

Проверка доступности mod_rewrite

Как включить RewriteEngine

О включении модуля mod_rewrite в конфигурационном файле Apache было рассказано в первой части. Если модуль включен, то его необходимо активировать в файле .htaccess директивой RewriteEngine:

RewriteEngine On

Это достаточно сделать один раз, даже если вы используете несколько правил перезаписи.

Для работы модуля также необходима активация опции FollowSymLinks. Эта опция может быть активирована в конфигурационном файле Apache (об этом также уже было сказано в первой части). Если эта опция отключена на уровне веб-сервера (или виртуального хоста), то её можно включить в файле .htaccess. Её нужно указать до директивы RewriteEngine:

Options +FollowSymLinks
RewriteEngine On

Как проверить, включён ли mod_rewrite

Как проверить в PHP включён mod_rewrite или нет

Самым простым способом является использование функции phpinfo(). Если модуль включён, то в таблице apache2handler в колонке Loaded Modules будет указано mod_rewrite (а также все другие модули, которые включены).

Этот способ является самым универсальным: вы можете использовать его в любой системе, в том числе на совместном (shared) хостинге.

Как проверить в Windows включён ли mod_rewrite

Откройте командную строку (Win+x, затем выберите Windows PowerShell). Перейдите в каталог, где размещены бинарные файлы Apache. Например, в моём случае это папка C:\Server\bin\Apache24\bin\:

cd C:\Server\bin\Apache24\bin\

И выполните там команду:

./httpd.exe -M

Будет выведен полный список модулей.

Как проверить в Linux включён ли mod_rewrite

Чтобы вывести список всех загруженных веб-сервером Apache модулей, используется опция -M. Исполнимый файл веб-сервера может называться apache2ctl или httpd в зависимости от используемого дистрибутива.

Для Debian, Ubuntu, Kali Linux, Linux Mint и их производных команда для вывода списка модулей следующая:

apache2ctl -M

Для Arch Linux, BlackArch и некоторых других дистрибутивов команда такая:

httpd -M

Проверка включён ли mod_rewrite с помощью .htaccess

В файле .htaccess запишите директиву:

RewriteEngine on

И попробуйте открыть адрес папки, где вы сохранили .htaccess, если возникнет ошибка «500 Internal server error», значит модуль mod_rewrite не включён в конфигурационном файле Apache.

Как сделать так, чтобы правила перезаписи использовались только если mod_rewrite включен

Конструкция <IfModule> проверяет, включён ли модуль. Если модуль включён, то выполняются директивы, которые находятся в секции <IfModule>…</IfModule>. Если модуль отключён, то эти директивы игнорируются. В результате, если модуль выключен, то неизвестные директивы не вызовут ошибку веб-сервера.

Синтаксис использования:

<IfModule mod_rewrite.c>
……
……
</IfModule>

Вместо многоточий запишите желаемые директивы mod_rewrite, пример:

<IfModule mod_rewrite.c>
     RewriteEngine On
     RewriteCond %{HTTP_USER_AGENT} ^HTTrack [NC,OR]
     RewriteCond %{HTTP_USER_AGENT} ^sqlmap [NC,OR]
     RewriteCond %{HTTP_USER_AGENT} ^wpscan [NC,OR]
     RewriteCond %{HTTP_USER_AGENT} ^text [NC]
     RewriteRule ^.* - [F]
</IfModule>

Перед именем модуля можно поставить ! (восклицательных знак) и тогда то, что внутри IfModule будет выполнено только если проверяемый модуль НЕ включён.

Секции <IfModule> можно использовать внутри другой секции <IfModule> и выполнять простые тестирования нескольких модулей в зависимости от условия предыдущих тестов модулей.

Эту секцию следуют использовать только если вам нужен один конфигурационный файл, которые работает независимо от того, доступен ли определённый модуль. При обычной работе директивы не должны размещаться в секциях <IfModule>.

Использование mod_rewrite для перенаправления (редиректа) и переназначения URL

Страница поменяла адрес, как показать новую страницу по старому адресу без редиректа

Описание:

Предположим, мы недавно переименовали страницу foo.html в bar.html и теперь хотим, чтобы старый URL также работал для обратной совместимости. Однако мы хотим, чтобы пользователи старого URL-адреса даже не узнали, что страницы были переименованы, то есть мы не хотим, чтобы адрес изменялся в их браузере.

Решение:

Мы с помощью RewriteRule делаем преобразования запроса, содержащего старый адрес, на новый, задав следующее правило:

RewriteEngine  on
RewriteRule    "^/foo\.html$"  "/bar.html" [PT]

В этом примере ^/foo\.html$ является регулярным выражением. Символы ^ и $ обозначают начало и конец строки соответственно. Перед точкой стоит слеш, чтобы символ трактовался буквально (как точка), а не как подстановочный символ (в качестве подстановочного символа точка означает любой один символ).

Страница поменяла адрес, как перенаправить на новую страницу при запросе старой (редирект)

Описание:

Предположим еще раз, что мы недавно переименовали страницу foo.html в bar.html и вновь хотим, чтобы старый URL работал для обратной совместимости. Но на этот раз мы хотим, чтобы пользователи старого URL-адреса получили намек на новый, т. е. поле адресной строки их веб-браузера должно измениться.

Решение:

Мы принудительно перенаправляем HTTP на новый URL-адрес, что приводит к изменению адреса страницы в браузере и, следовательно, того, что показано пользователю:

RewriteEngine  on
RewriteRule    "^/foo\.html$"  "bar.html" [R]

Кстати, для простых случаев редиректа можно использовать директиву Redirect. Эта директива не смогла бы заменить первый пример, когда мы показываем содержимое другой страницы без смены адреса (без редиректа). С Redirect второй пример выглядел бы так:

Redirect "/foo.html" "/bar.html"

Переадресация при смене домена

Описание:

Если сайт сменил домен, но сохранил прежнюю структуру страниц. Вы хотите, чтобы старые URL адреса продолжали работать пока пользователи не обновят их закладки.

Решение:

Вы можете использовать mod_rewrite для перенаправления этих URL на новый домен, но также рассмотрите вариант с использованием директив Redirect или RedirectMatch.

В этом примере будет производиться переадресация на новый домен с сохранением запроса, замените НОВЫЙ-САЙТ.ru на адрес сайта, куда должен выполняться редирект

RewriteEngine on
RewriteRule "(.+)" "https://НОВЫЙ-САЙТ.ru/$1" [R=301,L]
RewriteRule ^ "https://НОВЫЙ-САЙТ.ru/" [R=301,L]

Ещё один вариант, который также делает переадресацию на новый домен с сохранением структуры сайта, замените НОВЫЙ-САЙТ.ru на адрес сайта, куда должен выполняться редирект, а СТАРЫЙ-САЙТ.ru на имя старого домена:

RewriteEngine On
RewriteCond %{HTTP_HOST} ^СТАРЫЙ-САЙТ.ru$ [NC]
RewriteRule ^(.*)$ https://НОВЫЙ-САЙТ.ru/$1 [R=301,L]

В последующих примерах замените example.com на адрес сайта, куда должен выполняться редирект.

# С mod_rewrite
RewriteEngine on
RewriteRule   "^/docs/(.+)"  "http://new.example.com/docs/$1"  [R,L]

Правило означает найти запросы, которые содержат строку, которая начинается с /docs/ (символ ^ означает начало строки, а /docs/ - это буквальная последовательность символов), за которой затем следует что угодно (точка означает любой символ, а знак плюс означает один или более раз). Скобки образуют обратную ссылку. Т.е. то, что совпадает с выражением в скобках, можно использовать в дальнейшем, сославшись на это с помощью $1.

В строке перезаписи http://new.example.com/docs/ является буквальной частью, а $1 – это то, что совпало с частью выражения в скобках, т.е. обратная ссылка на (.+).

Таким образом, если был сделан запрос http://another.com/docs/best, то будет сделана переадресация на адрес http://new.example.com/docs/best.

# С RedirectMatch
RedirectMatch "^/docs/(.*)" "http://new.example.com/docs/$1"
# С Redirect
Redirect "/docs/" "http://new.example.com/docs/"

Директивы Redirect и RedirectMatch должы быть «легче» для сервера, но не всегда сложные случаи можно описать без использования mod_rewrite.

Простой редирект на новый сайт

Если сайт сменил домен и не сохранил структуру страниц, т.е. если вам нужно перенаправить все запросы на новый сайт (например, на его главную страницу), то это делается так:

RewriteEngine  on
RewriteRule ^ https://newsite.ru

В результате независимо от запрошенной страницы, все запросы будут переданы на главную страницу другого домена. Замените https://newsite.ru на тот сайт, куда вы перенаправляете запросы.

Как переправить все запросы из одной директории, в другую

Псевдоним для единичной директории:

RewriteEngine On
RewriteRule ^source-directory/(.*) /target-directory/$1 [R=301,L]

Все обращения к содержимому директории source-directory будут переадресованы к содержимому директории target-directory.

Использовать URL адресов без расширения файлов .php

Этот снипет позволяет вам использовать URL без расширения PHP, например, example.com/users вместо example.com/users.php.

RewriteEngine On
RewriteCond %{SCRIPT_FILENAME} !-d
RewriteRule ^([^.]+)$ $1.php [NC,L]

Универсальный документ ошибки (Error Document) для не найденных ресурсов (ошибка 404 Not Found)

Следующее правило выводит указанный вами файл в случае возникновения ошибки 404 Not Found. Обратите внимание, что вам самим нужно указать правильный код ответа HTTP 404 в заголовках ответа (в PHP коде, например).

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ /dir/error.php [END]

Если это правило перезаписи вызовет ошибку сервера, то замените флаг [END] на [L]. Флаг [END] подходит лучше, но поддерживается Apache 2.4 и не поддерживается версией Apache 2.2.

Вместо /dir/error.php нужно указать путь до файла, который вы хотите показывать в случае возникновения ошибки 404 (файл не найден).

Со статики на динамику

Описание:

Как мы можем трансформировать статичную страницу foo.html в динамичный вариант foo.cgi бесшовным образом, т.е. без уведомления браузера/пользователя.

Решение:

Мы просто переписываем URL на CGI-скрипт и принуждаем обработчик быть cgi-скриптом так, что он выполняется как CGI программа. Таким образом, запрос /~quux/foo.html внутренне приводить к вызову /~quux/foo.cgi.

RewriteEngine  on
RewriteBase    "/~quux/"
RewriteRule    "^foo\.html$"  "foo.cgi"  [H=cgi-script]

Обратная совместимость для изменений расширения файла

Описание:

Как мы можем сделать обратную совместимость URL (виртуально ещё существующих) после миграции document.YYYY в document.XXXX, например, после перехода ряда.html файлов на .php?

Решение:

Мы переписываем имя в его базовое имя и проверяем наличие файла с новым расширением. Если он существует, мы берем его, иначе URL используется в исходном состоянии.

#   набор правил для обратной совместимости
#   для перезаписи document.html на document.php
#   только тогда, когда document.php существует
<Directory "/var/www/htdocs">
    RewriteEngine on
    RewriteBase "/var/www/htdocs"

    RewriteCond "$1.php" -f
    RewriteCond "$1.html" !-f
    RewriteRule "^(.*).html$" "$1.php"
</Directory>

Обсуждение

В этом примере используется часто забываемая возможность mod_rewrite, вытекающая из порядка выполнения набора правил. В частности, mod_rewrite оценивает левую сторону RewriteRule (Шаблон поиска), прежде чем оценивать директивы RewriteCond. Следовательно, $1 уже определён к тому времени, когда оцениваются директивы RewriteCond. Это позволяет нам проверять наличие исходного (document.html) и целевого (document.php) файла с использованием того же базового имени файла.

Этот набор правил предназначен для использования в контексте директорий (в блоке <Directory> или в файле .htaccess), так что проверки -f смотрят в каталог по правильному пути. Возможно, вам потребуется установить директиву RewriteBase, чтобы указать базу каталогов, в которой вы работаете.

Замена на WebP изображения

Если поддерживаются WebP изображения, и изображение с файловым расширением .webp найдено в том же месте, где на сервере находится картинка jpg/png, то вместо неё будет отправлено изображение WebP.

RewriteEngine On
RewriteCond %{HTTP_ACCEPT} image/webp
RewriteCond %{DOCUMENT_ROOT}/$1.webp -f
RewriteRule (.+)\.(jpe?g|png)$ $1.webp [T=image/webp,E=accept:1]

Канонические имена хостов и URL. HTTPS

К одной и той же странице можно обратиться по-разному. Например, главную страницу сайта можно открыть любым из следующих методов:

  • http://www.w-e-b.site/
  • http://w-e-b.site/
  • http://www.w-e-b.site
  • http://w-e-b.site
  • http://www.w-e-b.site/index.php
  • http://w-e-b.site/index.php
  • http://w-e-b.site/?

Вариантов может быть даже больше, если сайт доступен и на HTTP, и на HTTPS. Также варианты могут возникнуть из-за различных ошибок составления ссылок, при которых страница продолжает открываться. Например:

  • http://www.yoursite.com//index.php

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

Поисковые системы стали значительно более продвинутыми, но не нужно рассчитывать только на них в вопросе борьбы с диблирующими страницами. К тому же, это может внести путаницу в аналитику (когда для каждого из перечисленного примера доход или посещаемостью считаются отдельно, хотя это одна и та же страница).

Поэтому веб-мастеру следует позаботиться о каноническом URL. На самом деле, нет никакой разницы, какую именно форму URL вы выберите в качестве канонической. Главное, выбрать что-то одно и придерживаться этого.

Как сделать редирект с HTTP на HTTPS

Помните, что для использования HTTPS протокола недостаточно просто сделать переадресацию, также должен быть настроен веб-сервер. То есть вы должны получить сертификаты и указать их в настройках хоста. Также веб-сервер должен быть настроен на прослушивание 443 порта. Если это всё готово, то для перенаправления на HTTPS, в файл .htaccess добавьте строки:

RewriteEngine on
RewriteCond %{HTTPS} !on
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI}

В этом примере переменная %{HTTPS} содержит on, если сайт использует HTTPS и содержит off, если используется HTTP. Таким образом, адрес страницы переписывается только если к ней обращаются по HTTP.

В RewriteRule в качестве шаблона поиска используется ^ - символ начала строки. Т.е. под это условие подпадают все строки. Цель переадресации указывается с помощью буквальной строки https:// и двух переменных окружения %{HTTP_HOST} и %{REQUEST_URI}.

Ещё один вариант записи, возможно, кому-то будет более простым для восприятия:

RewriteEngine on
RewriteCond %{HTTPS} off
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI}

В этом примере !on (не включено) заменено на off (выключено) и вместо ^ (начало строки) используется (.*) (означает «что угодно»). По сути эти две записи делают одно и то же.

Также на вашем HTTPS веб-сайте рекомендуется включить HTTP Strict Transport Security (HSTS) для помощи в предотвращении атак человек-посередине. Для этого достаточно добавить строки:

<IfModule mod_headers.c>
    Strict-Transport-Security: max-age=31536000; includeSubDomains
</IfModule>

Подробности: https://developer.mozilla.org/en-US/docs/Web/Security/HTTP_strict_transport_security

ВНИМАНИЕ: HSTS после включения трудно выключить. Поэтому HSTS включайте только осмысленно, когда вы точно уверены, что у вас не возникнет необходимости вновь откатываться с HTTPS до HTTP и вы уверены, что на сайте не должно быть страниц на HTTP. Либо вы понимаете, как отключить HSTS.

Как сделать редирект на с HTTP на HTTPS всех страниц кроме некоторых

Предположим, что нам нужно перевести на HTTPS все страницы кроме тех, которые находятся в папке /.well-known/, тогда используется следующая конструкция:

RewriteEngine on
RewriteCond %{HTTPS} !on
RewriteCond %{REQUEST_URI} !^/.well-known/
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI}

Замените /.well-known/ на желаемую папку или адрес страницы.

Если нужно исключить несколько страниц или каталогов, то составьте регулярное выражение с альтернативным выбором, т.е. с использованием трубы (|). Например, нужно включить переадресацию на HTTPS для всех страниц кроме находящихся в папке /.well-known/, в папке /test/, а также файла /stay-away.php:

RewriteEngine on
RewriteCond %{HTTPS} !on
RewriteCond %{REQUEST_URI} !^(/.well-known/|/test/|/stay-away.php)
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI}

Важно: если установлен заголовок Strict-Transport-Security:

<IfModule mod_headers.c>
        Header always set Strict-Transport-Security "max-age=15768000; includeSubDomains; preload"
</IfModule>

То правила с выборочным использованием или не использованием HTTPS для определённых страниц работать НЕ БУДУТ! Веб-браузер, получая этот заголовок, для всего сайта — для всех страниц и даже для всех субдоменов, включает HSTS (HTTP Strict Transport Security). С практической точки зрения это означает, что веб-браузер будет работать только с HTTPS страницами — открывать их сразу по HTTPS протоколу даже если ссылка или редирект указывают на использование HTTP протокол. Аналогичное правило распространяется на субдомены; если сертификат не поддерживает какой-либо субдомен сайта, то такой адрес будет невозможно открыть. Сайт с включённым HSTS невозможно добавить в исключения веб-браузера, чтобы он игнорировал ошибки неправильных SSL сертификатов.

Как сделать редирект на с HTTP на HTTPS только некоторых страниц

Если вам нужно перенаправить с HTTP на HTTPS только отдельные страницы, то подойдут показанные ранее примеры. Единственное необходимое в них изменение – убрать восклицательный знак (!), который служит для отрицания совпадения.

Для настройки редиректа на HTTPS только для папки /.well-known/

RewriteEngine on
RewriteCond %{HTTPS} !on
RewriteCond %{REQUEST_URI} ^/.well-known/
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI}

Для настройки редиректа на HTTPS только для папки /.well-known/, папки /test/, а также файла /stay-away.php:

RewriteEngine on
RewriteCond %{HTTPS} !on
RewriteCond %{REQUEST_URI} ^(/.well-known/|/test/|/stay-away.php)
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI}

Принудительное использование HTTPS за прокси

Полезно, если у вас есть прокси-сервер перед вашим сервером, отключающий TLS.

RewriteCond %{HTTP:X-Forwarded-Proto} !https
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI}

Всегда использовать WWW перед именем домена

Если вы хотите, чтобы в строке браузера перед названием домена всегда шло www, то используйте следующие правила:

RewriteEngine on
RewriteCond %{HTTP_HOST} ^example\.com [NC]
RewriteRule ^(.*)$ http://www.example.com/$1 [L,R=301,NC]

Обратите внимание, что example.com нужно заменить на домен вашего сайта, вместо протокола http:// может быть указано https://, а в строке ^example\.com слеш перед точкой не случаен – эта строка является регулярным выражением, чтобы точка рассматривалась не как подстановочный символ, а как буквальная точка, используется слеш.

Всегда использовать WWW перед именем домена – универсальный вариант

Этот вариант подойдёт без изменений для любых сайтов: не нужно указывать имя хоста (доменное имя), а также не нужно указывать, используется ли протокол HTTP или HTTPS. Т.е. это более универсальный вариант.

RewriteEngine On
RewriteCond %{HTTP_HOST} !=""
RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteCond %{HTTPS}s ^on(s)|
RewriteRule ^ http%1://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L]

Первое условие проверяет, не является ли значение Host пустым (в случае HTTP/1.0). Второе проверяет, не начинается ли Host на www..

Обратите внимание на RewriteCond %{HTTPS}s ^on(s)|. Здесь используется довольно хитрый приём. Как было сказано чуть выше, переменная окружения %{HTTPS} содержит on, если сайт использует протокол HTTPS, и содержит off, если используется HTTP. К переменной окружения добавлена буквальная буква s, в результате происходит проверка строки %{HTTPS}s, которая, в зависимости от того, включен ли HTTPS или нет, может сводиться к ons или offs. Эта строка сравнивается с регулярным выражением ^on(s)|, где ^ - это символ начала строки. Символ трубы (|) говорит о том, что подойдёт любая альтернатива – стоящая перед этим символом или после. Перед этим символом стоит строка on(s), а после – ничего. Пустая строка соответствует любой сравниваемой строке. Исходя из этого, результат RewriteCond всегда будет сводиться к истине. Но в зависимости от того, какая часть регулярного выражения совпала: on(s) или пустая строка, обратная ссылка будет иметь значение «s» или будет пустой строкой. Обратная ссылка задаётся скобками, в которых находится буква s.

В результате http%1 при RewriteRule будет сводиться к https или к http.

Таким образом, это правило подойдёт для любого сайта, в нём не нужно прописывать свой домен как это нужно делать в предыдущем. Также не нужно заботиться о протоколе сайта.

Никогда не использовать WWW перед именем домена

Если вам не нужно, чтобы перед доменом в строке браузера были буквы www, то используйте следующее правило:

RewriteEngine on
RewriteCond %{HTTP_HOST} ^www\.example\.com [NC]
RewriteRule ^(.*)$ http://example.com/$1 [L,R=301]

В нём замените http://example.com на имя вашего домена. Также обратите внимание на протокол. Во второй строке слеши используются для того, чтобы точки в регулярном выражении трактовались как буквальные символы (а не подстановочные).

Никогда не использовать WWW перед именем домена – универсальный вариант

Если вам нужно избавиться от www в адресной строке браузера, то следующее правило это сделает, переадресую запрос на аналогичный адрес, но без www:

RewriteEngine on
RewriteCond %{HTTP_HOST} ^www\.
RewriteCond %{HTTPS}s ^on(s)|off
RewriteCond http%1://%{HTTP_HOST} ^(https?://)(www\.)?(.+)$
RewriteRule ^ %1%3%{REQUEST_URI} [R=301,L]

В этом наборе условий и правила не нужно указывать свой домен – конструкция является универсальной для любого сайта, также подходит для сайтов на HTTP и HTTPS.

Принудительное использование канонического имени с HTTPS и www

Если ваш сайт работает через протокол HTTPS и в качестве канонического имени вы выбрали использовать www перед именем домена, то вам поможет любое из следующих правил. У них нет принципиальной разницы, если какоео-то из них не подошло для ваших условий, просто попробуйте другое.

Первый способ:

RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule .* https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteRule .* https://www.%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

В этом примере имеются два правила перезаписи. Первое перенаправляет на HTTPS. Второе правило перезаписывает любой запрос с неверным доменом на использование www. Флаг [NC] означает совпадение независимо от регистра.

Второй способ:

RewriteEngine On
RewriteCond %{HTTP_HOST} (?!^www\.)^(.+)$ [OR]
RewriteCond %{HTTPS} off
RewriteRule ^ https://www.%1%{REQUEST_URI} [R=301,L]

Третий способ:

RewriteEngine on
RewriteCond %{HTTP_HOST} !^$
RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteCond %{HTTPS}s ^on(s)|
RewriteRule ^ http%1://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
RewriteCond %{HTTPS} off
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=302,L,NE]

Четвёртый способ (замените domain.ru на свой домен):

RewriteEngine On
RewriteCond %{HTTP_HOST} !^www\.domain\.ru [NC]
RewriteRule ^(.*)$ https://www.domain.ru/$1 [R=301,L]
RewriteCond %{SERVER_PORT} 80
RewriteRule ^(.*)$ https://www.domain.ru/$1 [R=301,L]

Канонический вид с HTTPS и без www

Если ваш сайт работает на HTTPS, но вы не хотите видеть www в адресной строке браузера перед именем домена, то используйте:

RewriteEngine on
RewriteCond %{HTTPS} !on
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI}
RewriteCond %{HTTP_HOST} ^www\.
RewriteCond %{HTTPS}s ^on(s)|off
RewriteCond http%1://%{HTTP_HOST} ^(https?://)(www\.)?(.+)$
RewriteRule ^ %1%3%{REQUEST_URI} [R=301,L]

Принудительное SSL и www для главного домена, принудительное SSL без www для всех поддоменов (кроме локальных)

RewriteEngine On
# для поддоменов: принудительно ssl и без www
RewriteCond %{HTTP_HOST} !\.local$ [NC]
RewriteCond %{HTTPS} !=on
RewriteCond %{HTTP_HOST} !^(www\.)?domain\.ru$ [NC]
RewriteCond %{HTTP_HOST} ^(?:www\.|)(.*)$ [NC]
RewriteRule ^.*$ https://%1%{REQUEST_URI} [R,L]

# для главных доменов: принудительно ssl без www
RewriteCond %{HTTP_HOST} !\.local$ [NC]
RewriteCond %{HTTPS} !=on [OR]
RewriteCond %{HTTP_HOST} ^domain\.ru$ [NC]
RewriteRule ^.*$ https://www.domain.ru%{REQUEST_URI} [R,L]

Замените domain.ru на имя вашего домена.

Принудительное добавление конечного слеша к адресу сайта

Если вам нужно добавить к URL конечный слеш (в том случае, если он отсутствует), то воспользуйтесь этим правилом перезаписи:

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

Удаление конечного слеша

Этот сниппет перенаправит пути, заканчивающиеся на слеши, на аналогичные, но без конечного слеша (кроме действительных директорий), к примеру http://www.example.com/blog/ на http://www.example.com/blog. Это важно для SEO, поскольку рекомендуется иметь канонический URL для каждой страницы.

Если вам нужно убрать из URL конечный слеш, то для этого используйте:

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

Удаление конечных слешей из произвольных путей

Удаление конечных слешей из URL для веб-сайтов, размещённых в директории (как example.org/blog/):

RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} (.+)/$
RewriteRule ^ %1 [L,R=301]

Удаление лишних слешей в адресе URL

Например, страница /catalog///stranica.html доступна и открывается. Чтобы избежать такой ситуации и не плодить бесконечное число дублей следует записать следующий редирект:

RewriteEngine on
RewriteBase /
RewriteCond %{HTTP_HOST} !=""
RewriteCond %{THE_REQUEST} ^[A-Z]+\s/{2,}+(.*)\sHTTP/[0-9.]+$ [OR]
RewriteCond %{THE_REQUEST} ^[A-Z]+\s(.*)/{2,}+\sHTTP/[0-9.]+$
RewriteRule .* http://%{HTTP_HOST}%1 [R=301,L]

Это правило удаляет лишние слеши из начала и конца URL.

В этом правиле используется переменная %{THE_REQUEST}, она содержит полный запрос, примерно следующего вида:


GET /index.html HTTP/1.1

Для разделения частей запроса в выражениях условий, используется \s, которая означает белые пробелы.

В предыдущем правиле удаляются лишние слеши только в начале или конце запроса. Чтобы заменить два и более слеша в середине URL на одинарный слеш используйте следующее правило:

RewriteEngine on
RewriteBase /
RewriteCond %{HTTP_HOST} !=""
RewriteCond %{THE_REQUEST} ^[A-Z]+\s(.+)/{2,}+(.+)\sHTTP/[0-9.]+$
RewriteRule .* http://%{HTTP_HOST}%1/%2 [R=301,L]

Контроль доступа и блокировка хотлинка

Ограничение доступа по IP

Модуль mod_rewrite умеет переадресовывать, показывать различные страницы или блокировать доступ в зависимости от IP пользователя. Но если вам нужно просто заблокировать доступ для определённых IP, либо разрешить доступ определённым IP, но намного более удобным и лучшим вариантом будет использовать другой модуль, отвечающий за Контроль доступа к сайту (по ссылке подробная инструкция и множество примеров ограничения доступа к папка и отдельным файлам).

Запрет доступа к скрытым файлам и директориям

Скрытые файлы и директории (это те, чьи имена начинаются на точку .), должно в основном, если не всегда, быть защищены от просмотра веб-клиентами. Примеры таких файлов и папок: .htaccess, .htpasswd, .git, .hg

RewriteEngine on
RewriteCond %{SCRIPT_FILENAME} -d [OR]
RewriteCond %{SCRIPT_FILENAME} -f
RewriteRule "(^|/)\." - [F]

В качестве альтернативы, чтобы запутать атакующего, при попытке открыть такие файлы можно вызвать ошибку «Not Found».

RedirectMatch 404 /\..*$

Запрет хотлинка изображений

Хотлинк (англ. hotlink) – включение в веб-страницу файлов-изображений или других ресурсов с чужого сервера.

При использовании следующих правил вам нужно отредактировать домен example.com на имя вашего сайта.

Также при тестировании помните о кэшировании (если оно включено, то изображении некоторое время всё равно будет отдаваться из кэша).

RewriteEngine on
RewriteCond %{HTTP_REFERER} !^$
RewriteCond %{HTTP_REFERER} !^https?://(.+\.)?example.com [NC]
RewriteRule \.(jpe?g|png|gif|bmp)$ - [NC,F,L]

Приведённый выше вариант разрешит отправку изображений при пустом реферере («Blank Referrers»).

Что такое пустой реферер? Некоторые посетители имеют персональные файерволы или антивирусные программы, которые удаляют информацию о реферере (referrer) страницы, которую отправляет ваш веб-браузер. Защита от хотлинка основывается на этой информации. Поэтому если вы выберите запрет отправки изображений пользователям с пустым реферером, то вы заблокируете этих пользователей. Также это не позволит пользователям напрямую получать доступ к изображению, если они набрали его URL в браузере.

Допустим, вы не хотите разрешать «пустой реферер», тогда используйте следующий вариант:

RewriteEngine on
RewriteCond %{HTTP_REFERER} !^https?://(.+\.)?example.com [NC]
RewriteRule \.(jpe?g|png|gif|bmp)$ - [NC,F,L]

Допустим вы хотите показать изображение в духе «STOP HOTLINKING», тогда используйте следующий метод:

RewriteEngine on
# Удалите следующую строку, если вы хотите запретить доступ с пустым реферером
RewriteCond %{HTTP_REFERER} !^$
RewriteCond %{HTTP_REFERER} !^https?://(.+\.)?example.com [NC]
RewriteRule \.(jpe?g|png|gif|bmp)$ - [NC,F,L]

RewriteRule \.(jpe?g|png|gif|bmp) http://example.com/blocked.png [R,L]

Не забудьте поменять адрес изображения (http://example.com/blocked.png) на свой. Также убедитесь, что это изображение НЕ защищено от хотлинка, в противном случае ваш сервер попадёт в бесконечную петлю.

Запрет хотлинкинга только для определённых доменов

Иногда нужно отключить хотлинкинг изображений только для некоторых плохих парней. Для запрета хотлинка только от определённых доменов, таких как blockurl1.com, blockurl2.com и blockurl3.com, но разрешения любым другим сайтам вставлять ваши изображения:

RewriteEngine On
RewriteCond %{HTTP_REFERER} ^http://(.+\.)?blockurl1\.com/ [NC,OR]
RewriteCond %{HTTP_REFERER} ^http://(.+\.)?blockurl2\.com/ [NC,OR]
RewriteCond %{HTTP_REFERER} ^http://(.+\.)?blockurl3\.com/ [NC]
RewriteCond %{REQUEST_URI} !blocked\.gif$ [NC]
RewriteRule .*\.(jpe?g|gif|bmp|png)$ http://example.com/blocked.gif [L]

Вы можете добавить столько различных доменов, сколько вам нужно. Каждая строка RewriteCond должна заканчиваться флагами [NC,OR]. NC означает игнорировать регистр. OR означает логическое ИЛИ, т.е. правило сработает, если совпал этот домен или любой другой. Последний домен в списке идёт без флага OR, поскольку строки RewriteCond заканчиваются.

Последняя строка содержит URL "http://example.com/blocked.gif", который содержит изображение, которое будет показываться когда совпадут перечисленные условия – т.е. сработает запрет хотлинка.

Строка RewriteCond %{REQUEST_URI} !blocked\.gif$ [NC] ОТКЛЮЧАЕТ запрет хотлинка для изображения, которое показывается в случае срабатывания правил – это позволяет избежать бесконечного цикла.

Вы можете показывать сообщение об ошибке 403 Forbidden вместо изображения. Для этого замените последнюю строку в предыдущем примере на:

RewriteRule .*\.(jpe?g|gif|bmp|png)$ - [F]

Разрешение хотлинка для определённых сайтов

Чтобы разрешить хотлинк для определённых сайтов, укажите в следующих правилах свой сайт и сайты, которым вы хотите предоставить разрешение на вставку ваших картинок:

RewriteEngine on
RewriteCond %{HTTP_REFERER} !^https?://(.+\.)?example.com [NC]
RewriteCond %{HTTP_REFERER} !^http://(www\.)?goodsite\.com/.*$ [NC]
RewriteRule .*\.(jpe?g|gif|bmp|png)$ - [F]

Блокировка пользователя по рефереру (Referrer)

Блокировка пользователей на основе ссылающегося домена. Это запрещает доступ для всех пользователей, кто пришёл (отправлен с) определённого домена:

RewriteEngine on
RewriteCond %{HTTP_REFERER} somedomain\.com [NC,OR]
RewriteCond %{HTTP_REFERER} anotherdomain\.com
RewriteRule .* - [F]

Замените somedomain.com и anotherdomain.com на действительные значения доменов (сайтов), которые вы не любите.

Бывают ситуации, когда негативный трафик идёт с определённых сайтов, например, с буксов или просто с сайтов, которые вам не нравятся. В некоторых случаях с такими переходами можно бороться, но не всегда.

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

Но если переход делается непосредственно с сайта букса, либо показывается в iframe, то с таким можно бороться.

Также если ваш сайт добавили в агрегатор или разместили ссылку на сайте, который вам не нравится, то этот метод также сработает.

К примеру плохим сайтом является https://site.click/. Чтобы заблокировать переходы с этого сайта, можно использовать следующее:

RewriteCond %{HTTP_REFERER} https://site.click/ [NC]
RewriteRule .* - [R=404]

В этом случае всем, кто пришёл с сайта https://site.click/ будет показываться сообщение «404 страница не найдена». При желании, можно поставить любой другой код ответа вместо 404, например, 403 (доступ запрещён), 500 (внутренняя ошибка сервера) или любой другой.

При желании заблокировать доступ с нескольких сайтов, используйте флаг [OR], например:

RewriteCond %{HTTP_REFERER} https://site.click/ [NC,OR]
RewriteCond %{HTTP_REFERER} anotherdomain\.com [NC,OR]
RewriteCond %{HTTP_REFERER} andanotherdomain\.com [NC,OR]
RewriteCond %{HTTP_REFERER} onemoredomain\.com [NC]
RewriteRule .* - [R=404]

Обратите внимание, в последней строке не нужно указывать флаг [OR].

Вместо вывода ошибки, можно сделать редирект на любую страницу своего сайта, например, в следующем случае все пришедшие с сайта https://site.click/ пользователи будут отправлены на страницу error.html вашего сайта:

RewriteCond %{HTTP_REFERER} https://site.click/ [NC]
RewriteRule .* error.html [R]

А в следующие правила устанавливают всех пришедших с сайта https://site.click/ отправлять на https://natribu.org/ru/:

RewriteCond %{HTTP_REFERER} https://site.click/ [NC]
RewriteRule .* https://natribu.org/ru/ [R]

Блокировка плохих ботов, клонеров сайтов, офлайн браузеров

Для отключения доступа ботам и другим программам:

RewriteEngine on
RewriteBase /
RewriteCond %{HTTP_USER_AGENT} ^Anarchie [OR]
RewriteCond %{HTTP_USER_AGENT} ^ASPSeek [OR]
RewriteCond %{HTTP_USER_AGENT} ^attach [OR]
RewriteCond %{HTTP_USER_AGENT} ^autoemailspider [OR]
RewriteCond %{HTTP_USER_AGENT} ^Xaldon\ WebSpider [OR]
RewriteCond %{HTTP_USER_AGENT} ^Xenu [OR]
RewriteCond %{HTTP_USER_AGENT} ^Zeus.*Webster [OR]
RewriteCond %{HTTP_USER_AGENT} ^Zeus
RewriteRule ^.* - [F,L]

Обратите внимание, что список неполный и (возможно), неактуальный. Дополняйте/редактируйте список на основе анализа логов ваших веб-сайтов.

Если вместо блокировки доступа, вы хотите отправлять их на какие-нибудь адские веб-сайты по вашему выбору, то замените последнюю строку на:

RewriteRule ^.*$ https://www.nsa.com [R,L]

Или отправьте их на виртуальную чёрную дыру фальшивых email адресов:

RewriteRule ^.*$ http://english-61925045732.spampoison.com [R,L]

Запрет доступа к файлу или директории в определённые периоды времени

Закрытие доступа в полночь:

RewriteCond %{TIME_HOUR} ^12$
RewriteRule ^.*$ - [F,L]

Закрытие доступа с 12 до 15 часов:

RewriteCond %{TIME_HOUR} ^(12|13|14|15)$
RewriteRule ^.*$ - [F,L]

Следующий набор директив запрещает доступ с 18 часов до 7 часов утра. При попытке посетить сайт в этот промежуток времени, будет выдан ответ 403 Forbidden (флаг [F]):

RewriteEngine On
RewriteCond "%{TIME_HOUR}" ">=18" [OR]
RewriteCond "%{TIME_HOUR}" "<07"
RewriteRule "^" "-" [F]

Запрет доступа с пустым реферером (Referer)

Следующее правило запретит доступ всем запросом, в котором не установлен HTTP заголовок Referer (в данном случае в логах Apache вместо строки Referer записывается "-"):

RewriteEngine	on
RewriteCond	%{HTTP_REFERER}	^$
RewriteRule	^.*	-	[F,L]

Блокировка доступа по части пользовательского агента (User Agent)

Уже рассмотрены примеры блокировки ботов по User Agent, когда имя пользовательского агента короткое и представляет собой одну строку без пробела.

Необязательно указывать полное имя — можно указать только часть строки User Agent для совпадения. Специальные символы и пробелы должны быть экранированы.

Например, следующее правило заблокирует доступ для всех пользователей, в чьей строке User Agent встречается «Android 10»:

RewriteEngine	on
RewriteCond	%{HTTP_USER_AGENT}	"Android\ 10"
RewriteRule	^.*	-	[F,L]

Примеры заблокированных этим правилом User Agent:

  • Mozilla/5.0 (Linux; Android 10; SM-G970F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.66 Mobile Safari/537.36
  • Mozilla/5.0 (Linux; Android 10; Redmi Note 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.66 Mobile Safari/537.36

Как заблокировать доступ по точному совпадению User Agent

Если вам нужно заблокировать доступ к сайту определённым User Agent с точным совпадением имени, то используйте конструкцию с If:

<If "%{HTTP_USER_AGENT} == 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)'">
	Require all denied
</If>

<If "%{HTTP_USER_AGENT} == 'Mozilla/5.0 (Windows NT 10.0; WOW64; rv:45.0) Gecko/20100101 Firefox/45.0'">
	Require all denied
</If>

<If "%{HTTP_USER_AGENT} == 'Mozilla/5.0 (Windows NT 6.1; rv:45.0) Gecko/20100101 Firefox/45.9.0'">
	Require all denied
</If>

If доступна начиная с Apache 2.4.

Запрет доступа к определённым страницам

Переменная %{REQUEST_URI} включает в себя всё, что идёт в запросе после имени хоста (но не включает то, что идёт после знака вопроса в URL), используя её можно фильтровать запросы по URL, строке запроса, именам файла или их частям. Например:

RewriteEngine	on
RewriteCond	%{REQUEST_URI}	"строка-запроса"
RewriteRule	^.*	-	[F,L]

Не смотря на то, что в логах веб-сервера Apache некоторые символы, в том числе кириллица, отображается в URL кодировке, в данных правилах можно указывать кириллицу. Например, следующее правило заблокирует доступ к статье с URL https://zawindows.ru/как-узнать-какой-процесс-блокирует-фа/:

RewriteEngine	on
RewriteCond	%{REQUEST_URI}	"как-узнать-какой-процесс"
RewriteRule	^.*	-	[F,L]

При желании, можно указать сразу несколько URL (или их частей). Каждая строка для поиска должна быть помещена в круглые скобки, между собой строки в скобках должны быть разделены символом | (конвейер, труба), например:

RewriteEngine	on
RewriteCond	%{REQUEST_URI}	"(проигрыватель-windows)|(как-узнать-какой-процесс)|(сколько-оперативной-памяти)|(как-в-windows-10-открывать-иконки)|(7-приложений-для-подделки-вашего)"
RewriteRule	^.*	-	[F,L]

Поскольку %{REQUEST_URI} не включает то, что идёт после знака вопроса в URL, то для фильтрации по строке запроса, идущей после знака вопроса, используйте %{QUERY_STRING}. Об этом смотрите раздел «Как фильтровать по строке запроса, идущей после знака вопроса».

Запрет доступа IP и диапазонам

С помощью mod_rewrite можно блокировать отдельные IP от доступа к сайту:

RewriteEngine	on
RewriteCond	"%{REMOTE_ADDR}"	"84.53.229.255"
RewriteRule	^.*	-	[F,L]

Можно указать несколько IP адресов для блокировки:

RewriteEngine	on
RewriteCond	"%{REMOTE_ADDR}"	"84.53.229.255" [OR]
RewriteCond	"%{REMOTE_ADDR}"	"123.45.67.89" [OR]
RewriteCond	"%{REMOTE_ADDR}"	"122.33.44.55"
RewriteRule	^.*	-	[F,L]

Также можно использовать и диапазоны, но нужно помнить, что в данном случае строки расцениваются как регулярные выражения, но тесть нотация CIDR (например, 94.25.168.0/21) не поддерживается.

Диапазоны должны быть указаны как регулярные выражения — это можно сделать с использованием наборов символов. Например, для блокировки следующих диапазонов

  • 94.25.168.0/21 (диапазон 94.25.168.0 - 94.25.175.255)
  • 83.220.236.0/22 (диапазон 83.220.236.0 - 83.220.239.255)
  • 31.173.80.0/21 (диапазон 31.173.80.0 - 31.173.87.255)
  • 213.87.160.0/22 (диапазон 213.87.160.0 - 213.87.163.255)
  • 178.176.72.0/21 (диапазон 178.176.72.0 - 178.176.75.255)

будет работать правило:

RewriteEngine	on
RewriteCond	"%{REMOTE_ADDR}"	"(94\.25\.1[6-7]])|(83\.220\.23[6-9])|(31\.173\.8[0-7])|(213\.87\.16[0-3])|(178\.176\.7[2-5])"
RewriteRule	^.*	-	[F,L]

Обратите внимание, что диапазон 94.25.168.0 — 94.25.175.255 нельзя записать как 94.25.1[68-75], это будет истолковано как строка «94.25.1», и набор символов, включающий в себя символ 6, диапазон 8-7 и символ 5. Из-за диапазона 8-7 данная запись вызовет ошибку на сервере.

Поэтому для записи 94.25.168.0 — 94.25.175.255 используется «94\.25\.1[6-7]».

Также обратите внимание, что последний октет 0-255 можно пропускать, поскольку для совпадения с регулярным выражением достаточно того, что совпадёт часть IP адреса.

Комбинирование правил контроля доступа

Задание: заблокировать пользователей, удовлетворяющих сразу ВСЕМ последующим критериями:

1. Пустой реферер

2. Пользовательский агент содержит строку «Android 10»

3. Доступ был сделан к странице, URL которой содержит любую из строк

  • проигрыватель-windows
  • как-узнать-какой-процесс
  • сколько-оперативной-памяти
  • как-в-windows-10-открывать-иконки
  • 7-приложений-для-подделки-вашего

4. Пользователь имеет IP адрес, принадлежащий любому из диапазонов:

  • 94.25.168.0/21 (диапазон 94.25.168.0 - 94.25.175.255)
  • 83.220.236.0/22 (диапазон 83.220.236.0 - 83.220.239.255)
  • 31.173.80.0/21 (диапазон 31.173.80.0 - 31.173.87.255)
  • 213.87.160.0/22 (диапазон 213.87.160.0 - 213.87.163.255)
  • 178.176.72.0/21 (диапазон 178.176.72.0 - 178.176.75.255)

Следующий набор правил будет соответствовать указанной задаче:

RewriteEngine	on
RewriteCond	"%{REMOTE_ADDR}"	"(94.25.1[6-7]])|(83.220.23[6-9])|(31.173.8[0-7])|(213.87.16[0-3])|(178.176.7[2-5])"
RewriteCond	%{HTTP_REFERER}	^$
RewriteCond	%{HTTP_USER_AGENT}	"Android\ 10"
RewriteCond	%{REQUEST_URI}	"(проигрыватель-windows)|(как-узнать-какой-процесс)|(сколько-оперативной-памяти)|(как-в-windows-10-открывать-иконки)|(7-приложений-для-подделки-вашего)"
RewriteRule	^.*	-	[F,L]

Обратите внимание, что правила, которые связаны логическим ИЛИ, должны быть собраны в одно большое правило. То есть ни с одним из правил нельзя использовать флаг [OR], иначе это сломает логику всего набора правил.

Пример правила блокировки всех пользователей с пустым Referer, кроме одного IP адреса (127.0.0.1):

RewriteEngine   on
RewriteCond "%{REMOTE_ADDR}"	"!127.0.0.1"
RewriteCond %{HTTP_REFERER}	^$
RewriteRule ^.* -	[F,L]

Как фильтровать по строке запроса, идущей после знака вопроса

Переменная %{QUERY_STRING} содержит строку запроса, которая следует после символа ? (знак вопроса) текущего запроса к серверу.

Обратите внимание, что фильтруемое значение должно быть в URL кодировке. К примеру, следующее правило:

RewriteCond %{QUERY_STRING} "p=5373&%D0%B7%D0%B0%D0%B1%D0%BB%D0%BE%D0%BA%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D1%82%D1%8C"
RewriteRule ^.* - [F,L]

Заблокирует доступ к странице https://suay.ru/?p=5373&заблокировать, но не запретит доступ к странице https://suay.ru/?p=5373.

Обработка строки параметров URI в mod_rewrite

Источник: Обработка строки параметров URI в mod_rewrite (цитируется с разрешения)

Запросы: request и query

Чтобы не возникло путаницы, начнём с терминов. В английском языке используются два слова request и query. Проблема в том, что они переводятся одинаково как «запрос». При этом они могут использоваться в одном контексте — в английском языке это не вызывает путаницы, а в русском получается «запрос» и «запрос», которые означают разные вещи.

Слово request относится к HTTP запросу. Протокол HTTP передаёт запрос на сервер, в этом запросе содержаться заголовки (например, информация об интересующем хосте (сайте), User-Agent пользователя, имеющиеся для этого сайта кукиз и прочее). Также говорят об URI запроса, методе запроса и так далее. Среди передаваемой информации также имеется query — строка запроса.

К примеру, если я в веб-браузере открою страницу suay.ru/wp-admin/post.php?post=1673&action=edit, то в ней строкой запроса (query) будет post=1673&action=edit, то есть всё то, что следует после ? (знака вопроса).

Как в mod_rewrite искать по параметрам после знака вопроса

Нужно понимать эту разницу, так как по умолчанию mod_rewrite работает с REQUEST_URI, то есть частью запроса, которая НЕ включает в себя строку query. Причём RewriteRule просто игнорирует query.

Очень часто mod_rewrite используется для ЧПУ («красивых» адресов), когда нужно сделать преобразования вида: из

  • site.com/phones/infos/4/52

в

  • site.com/index.php?category=phones&subcat=infos&idc=4&marea=52

В этом случае, как мы видим, исходная строка не содержит строки запроса (query), поэтому при написании правила проблема игнорирования query не возникает:

RewriteEngine on
RewriteRule ([A-Za-z]+)/([A-Za-z]+)/([0-9]+)/([0-9]+) index.php?category=$1&subcat=$2&idc=$3&marea=$4

А что если нам нужно сделать преобразование другого рода:

Из:

http://site.com/category/infos/pages.php?idc=4&marea=52

В:

http://site.com/category/infos/pages.php?marea=52

То есть мы убираем из строки запроса (query) параметр idc вместе с его значением.

В этом случае мы уже не можем использовать RewriteRule, поскольку его правила полностью игнорируют строку query. Правда, используя некоторые флаги можно сделать так, чтобы исходная строка запроса query добавлялась к новому URI — но нам в рассматриваемой ситуации нужно не это.

Проблема решается с использованием директивы RewriteCond, которая умеет работать с переменной QUERY_STRING, содержащей строку query с запросом.

Что нам нужно знать про RewriteCond? Директива RewriteCond оказывает воздействие на RewriteRule, которое следует после строки (или блока строк) RewriteCond. В RewriteCond также можно использовать обратные ссылки, но для разграничения от обратных ссылок RewriteRule, вместо $ (знака доллара) используется % (знак процента), то есть вместо $1, первая обратная ссылка обозначается как %1.

Итак, составляем RewriteCond для нашего случая:

RewriteCond "%{QUERY_STRING}" "idc=([0-9]+)&marea=([0-9]+)$"

Рассмотрим эту строку подробно.

%{QUERY_STRING} означает, что анализируется строка запроса, следующая в URI после знака вопроса.

Строка idc=([0-9]+)&marea=([0-9]+)$ представляет собой шаблон поиска. То есть ищется строка, которая начинается на idc=, затем следуют цифры (одна или более) — причём, поскольку это регулярное выражение в скобках, то найденные цифры помещаются в первую обратную ссылку, затем идёт строка &marea=, затем опять цифры (одна или более), причём найденные цифры помещаются во вторую обратную ссылку. В данном случае символ $ означает конец анализируемой строки.

Чего мы этим добились? Мы сделали так, что значение idc будет помещено в первую обратную ссылку, а значение marea будет помещено во вторую обратную ссылку.

Переходим непосредственно к правилу перезаписи RewriteRule:

RewriteRule "(.*)" "/$1?marea=%2" [L,R]

Рассмотрим это содержимое подробно:

Регулярное выражение (.*) означает «что угодно». Но мы помним, что это «что угодно» игнорирует строку запроса query. То есть это URI без строки query. Причём, поскольку это регулярное выражение в скобках, то оно помещается в первую обратную ссылку.

Далее строка /$1?marea=%2. Она начинается с указания на корневую папку сайта (/), затем идёт первая обратной ссылка $1, в неё помещается весь URI кроме строки запроса, затем добавляется строка ?marea=, к которой добавляется то, что содержится во второй обратной ссылке (%2) от RewriteCond.

Флаги [L,R] означают завершить проверку по другим правилам и сделать редирект на новый адрес (то есть адрес страницы измениться в адресной строке веб-браузера пользователя).

В результате получится, что адрес:

  • http://site.com/category/infos/pages.php?idc=4&marea=52

Превратится в:

  • http://site.com/category/infos/pages.php?marea=52

Соберём условие перезаписи и правило перезаписи вместе:

RewriteEngine on
RewriteCond "%{QUERY_STRING}" "idc=([0-9]+)&marea=([0-9]+)$"
RewriteRule "(.*)" "/$1?marea=%2" [L,R]

Как с mod_rewrite удалить параметр из URI

Эта конструкция приводит к тому, что будет «вырезано» значение определённого параметра из получаемой строки запроса. Если говорить более точно, строка запроса будет заново пересобрана, но уже без одного из своих параметров. Используя эту технику можно убирать один или несколько параметров из запроса, либо присваивать их значения другим параметрам.

RewriteEngine    On
RewriteCond      %{QUERY_STRING}    ^page=1$
RewriteRule      (.*)               $1?     [R=permanent]

Построчно:

  1. Вы включаете функцию перезаписи.
  2. В качестве условия («оператор если») вы указываете, что строка запроса должна быть точно page=1, чтобы применялись следующие правила.
  3. Затем вы указываете правило, которое говорит, что нужно заменить весь путь (.*) на него же ($1), но сделать строку запроса пустой (?) и на этот результат выполняется постоянный редирект permanent redirect (301).

Если вы хотите, чтобы перенаправление было временным (302), вы можете просто удалить часть =permanent. Moved Temporarily (временно перемещено) это значение по умолчанию для флага R.

Перемещено Временно - значение по умолчанию для флага R.

Как удалить часть строки запроса после знака вопроса

Выше показан пример когда удаляется вся строка запроса. Но что если нужно удалить только один параметр из строки запроса URI, а другой оставить?

Чтобы было понятнее, рассмотрим следующую ситуацию: необходимо из адреса страницы удалить строку «&PageSpeed=noscript», при этом необходимо сохранить параметр «p=».

Рассмотрим конкретный пример. Необходимо, чтобы страница с адресом:

https://site.net/?p=6413&PageSpeed=noscript

была переадресована на страницу с адресом:

https://site.net/?p=6413

Эту задачу решает следующее правило:

RewriteEngine    On
RewriteCond      %{QUERY_STRING}    "p=(.+)&PageSpeed=noscript"
RewriteRule      (.*)               /$1?p=%1     [R=permanent]

Вторая строка ищет в страке запроса совпадение с шаблоном «p=(.+)&PageSpeed=noscript». Это означает, что за переменной «p=» может следовать что угодно (в шаблоне обозначается как «.+»), а затем следует строка «&PageSpeed=noscript». Поскольку часть шаблона со значением «что угодно» помещена в круглые скобки (.+), то оно становится значением обратной ссылки.

Затем (в третьей строке) указано правило, которое говорит, что нужно заменить весь путь (.*) на него же $1, а затем добавить строку «?p=» после которой будет помещено значение обратной ссылки %1 из %{QUERY_STRING}. То есть заново формируется строка запроса с прежним значением «p=», но при этом строка «&PageSpeed=noscript» оказывается отброшенной.

Таким же образом, используя обратные ссылки с номерами два (%2), три (%3) и так далее можно сохранить значение нескольких переменных из строки запроса, а остальные удалить с помощью mod_rewrite.

Поскольку для указанного сайта значением «p=» может быть только число, то вместо указания «что угодно» (.+) можно указать диапазон допустимых символов (в данном случае только цифры) как ([0-9]+). В результате правило будет выглядеть так:

RewriteEngine    On
RewriteCond      %{QUERY_STRING}    "p=([0-9]+)&PageSpeed=noscript"
RewriteRule      (.*)               /$1?p=%1     [R=permanent]

mod_rewrite: отрицание в строке запроса %{QUERY_STRING}

Рассмотрим отрицание в строках запроса, то есть когда правило RewriteRule применяется только к запросам, в которых у query отсутствует определённая строка. Для этого перед Шаблоном укажите ! (восклицательный знак) или конструкцию !=. Также Шаблон не нужно помещать в кавычки, иначе условие перестаёт работать. В результате RewriteRule будет применено только к запросам без указанной строки в query (в данном случае без fromsubdomain=true).

RewriteEngine On
RewriteCond "%{QUERY_STRING}" !=fromsubdomain=true [NC]
RewriteRule subdomain/([^/]+) https://$1.suay.ru

Как в mod_rewrite переписать строку запроса

Далее идёт перевод официальной документации.

Описание:

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

Решения:

Многие из решений в этом разделе будут использовать одно и то же условие, которое оставляет совпадающее значение в обратной ссылке %2 . %1 – это начало строки запроса (вплоть до интересующего ключа), а %3 – это остаток. Чтобы быть гибким и избегать двойных «&&» в подстановках условие получилось немного сложным.

Это решение удаляет соответствующий ключ и значение:

# Удалить mykey=???
RewriteCond "%{QUERY_STRING}" "(.*(?:^|&))mykey=([^&]*)&?(.*)&?$"
RewriteRule "(.*)" "$1?%1%3"

В приведённом выше решении в RewriteCond создаются три обратные ссылки: первая (%1) содержит то, что до mykey, вторая – содержит значение mykey, третья (%3) содержи значение других переменных, которые после mykey.

В результате перезаписи адрес до строки запроса остаётся не изменным (это обратная ссылка $1, которая указывает на то, что нашёл RewriteRule), затем через знак вопроса '?' дописываются обратные ссылки %1 и %3 – которые составляют исходную строку запроса, но уже без mykey.

Следующее решение использует захваченное значение mykey при создании нового URL, отбрасывая остальную часть исходной строки запроса; на конце добавляется '?':

# Скопировать из строки запроса в PATH_INFO
RewriteCond "%{QUERY_STRING}" "(.*(?:^|&))mykey=([^&]*)&?(.*)&?$"
RewriteRule "(.*)" "$1/products/%2/?" [PT]

Это решение проверяет захваченное значение в последующем условии:

# Захват значения mykey в строке запроса
RewriteCond "%{QUERY_STRING}" "(.*(?:^|&))mykey=([^&]*)&?(.*)&?$"
RewriteCond "%2" !=not-so-secret-value
RewriteRule "(.*)" - [F]

Это решение показывает обратную сторону предыдущих, копируя компоненты пути (возможно, PATH_INFO) из URL-адреса в строку запроса.

Это решение преобразовывает путь до страницы в строку запроса:

# Желаемый URL должен быть /products/kitchen-sink, а скрипт ожидает
# /path?products=kitchen-sink.
RewriteRule "^/?path/([^/]+)/([^/]+)" "/path?$1=$2" [PT]

Ошибка ERR_TOO_MANY_REDIRECTS при удалении строки запроса. Как использовать флаг QSD для удаления строки запроса

Рассмотрим следующий пример, который удаляет из запроса строку «next=0», но сохраняет значение переменной «cat=»:

RewriteEngine    On
RewriteCond      %{QUERY_STRING}    "^cat=(.+)&next=0$"
RewriteRule      (.*)               /$1?cat=%1     [R=permanent]

При тестах это правило работает как ожидается.

Немного изменим это правило, чтобы удалять строку «next=0» даже из запросов, в которых отсутствует «cat=»:

RewriteEngine    On
RewriteCond      %{QUERY_STRING}    "^next=0$"
RewriteRule     (.*)               /$1     [R=permanent]

В этом примере если в запросе имеется строка «next=0» и при этом этом другие переменные отсутствуют, то делается перенаправление на главную страницу.

То есть при попытке открыть адрес

https://site.net/?next=0

должна быть сделана переадресация на главную страницу сайта:

https://site.net/

Вместо ожидаемого результата, этот как казалось бы более простой пример выдаёт неожиданную ошибку:

This page isn’t working
web.site redirected you too many times.
Try deleting your cookies.
ERR_TOO_MANY_REDIRECTS

Суть ошибки в бесконечных редиректах (перенаправлениях, переадресации). То есть вместо того, перейти на главную страницу, правило mod_rewrite отправляет пользователю на один и тот же адрес снова и снова.

Чтобы понять причину ошибки, нужно знать правила, по которым со строкой запроса работает веб-сервер Apache. Эти правила следующие:

  1. По умолчанию, строка запроса сохраняется и пристыковывается к конечному полученному результату.
  2. Если пользователь как-либо указал собственную строку запроса, то начальная строка запроса полностью удаляется и используется то, что создал пользователь.
  3. Даже если пользователь не указал собственную строку запроса, с помощью флага QSD можно поменять поведение по умолчанию и отбрасывать строку запроса.

Подробности о флаге QSD|qsdiscard (отбросить любую строку запроса из входящего URI) смотрите по следующему адресу: https://hackware.ru/?p=5564#310

Итак, теперь становится понятно, что хотя мы и не указывали сохранять строку запроса в последнем примере, она каждый раз добавлялась к полученному результату. В результате происходила переадресация страницы на саму себя. Это выполнялось бесконечное количество раз.

В рабочем примере приведённом немного выше, мы не столкнулись с этой проблемой поскольку для сохранения переменной «cat=» мы указывали новую строку запроса, а старая отбрасывалась.

Итак, если мы не создаём новую строку запроса и хотим полностью отбросить старую, есть как минимум два способа это сделать.

Во-первых, можно использовать флаг QSD (Query String Discard) для перенаправления без передачи строки запроса:

RewriteEngine    On
RewriteCond      %{QUERY_STRING}    "^next=0$"
RewriteRule     (.*)               /$1     [R=permanent]

Чуть видоизменённое правило (для улучшения его читаемости):

RewriteEngine    On
RewriteCond      %{QUERY_STRING}    "^next=0$"
RewriteRule      .*               /     [QSD,R=permanent]

Помните, что флаг QSD доступен с Apache версии 2.4.0 и более поздних. Версии Apache до 2.4, которые не поддерживают QSD, при использовании этого флага вернут ошибку 500.

Вторым, более универсальным вариантом, в том числе подходящим для более ранних версий Apache, является использование знака вопроса (?) в конце целевого URL-адреса. Это эквивалентно указанию пустой строки запроса.

Пример правила mod_rewrite, которое полностью удаляет строку запроса, если она соответствует указанному шаблону:

RewriteEngine    On
RewriteCond      %{QUERY_STRING}    "^next=0$"
RewriteRule      .*               /?     [R=permanent]

Этот вариант работает на любых версиях Apache.

Перенаправление портов

Как перенаправить запросы на 80-й порт

Если ваш сервер принимает запросы не только на стандартные порты, то вы можете сделать перенаправление.

Чтобы переправить все запросы, которые пришли не на 80-й порт:

RewriteCond "%{HTTP_HOST}"   "!^www\.example\.com"       [NC]
RewriteCond "%{SERVER_PORT}" "!^80$"
RewriteRule "^/?(.*)"        "https://example.com/$1" [L,R,NE]

Как перенаправить запросы на 443-й порт

Чтобы переправить все запросы, которые пришли не на 443-й порт:

RewriteCond "%{HTTP_HOST}"   "!^www\.example\.com"       [NC]
RewriteCond "%{SERVER_PORT}" "!^443$"
RewriteRule "^/?(.*)"        "https://example.com/$1" [L,R,NE]

Как перенаправить запросы на 80-й порт кроме некоторых страниц

Чтобы переправить все запросы, которые пришли не на 80-й порт, но сделать исключение для одной страницы (act=proxy-checker):

RewriteCond "%{HTTP_HOST}"   "!^www\.example\.com"       [NC]
RewriteCond %{QUERY_STRING} !act=proxy-checker
RewriteCond "%{SERVER_PORT}" "!^80$"
RewriteRule "^/?(.*)"        "https://suip.biz/$1" [L,R,NE]

В результате все запросы, пришедшие не на 80-й порт будут перенаправлены на него, но запрос страницы сайт.ru/act=proxy-checker будет обработан на любом прослушиваемом порту.

Как в mod_rewrite читать параметры POST

mod_rewrite не может работать с данными, передаваемыми методом POST. То есть с помощью mod_rewrite не получится модифицировать запросы POST, либо заблокировать или выполнить другие действия на основе запросов, передаваемых методом POST.

В качестве альтернативы используйте обработку данных POST в скриптах вашего сайта (с помощью PHP) — это простой способ, либо с помощью модуля Apache mod_security — это очень сложный способ.

Продолжение: «Полное руководство по mod_rewrite (часть 6): Продвинутые техники применения mod_rewrite».


Рекомендуется Вам:

27 комментариев to Полное руководство по mod_rewrite (часть 5): Частые случаи и примеры использования mod_rewrite

  1. Алёша:

    Как вырезать часть (idc=4&) в адресе? Например есть страница: http://site.com/category/infos/pages.php?idc=4&marea=52 Нужно сделать переадресацию на: http://site.com/category/infos/pages.php?marea=52 Помогите тугодуму.

    • Alexey:

      Следующая конструкция сделает то, что вам нужно:

      RewriteEngine on
      RewriteCond "%{QUERY_STRING}" "idc=([0-9]+)&marea=([0-9]+)$"
      RewriteRule "(.*)" "/$1?marea=%2" [L,R]

      Подробное объяснение как это получилось и как работает в статье: https://suay.ru/?p=1777

  2. Павел:

    Здравствуйте. А как правильно сделать переадресацию с site.ru/k1 на site.ru/?utm_source=yandex&utm_medium=link ?

    Поробовал вот так, но половина сайта перестало работать, кроме главной страницы.

    <IfModule mod_rewrite.c>

     RewriteEngine on

    RewriteBase /k1

    RewriteRule ^index\.php$ /?utm_source=yandex&utm_medium=link [R=301,L]

    #</IfModule>

  3. Дима:

    Этот пример не работает

    RewriteEngine On
    RewriteCond %{SCRIPT_FILENAME} !-d
    RewriteRule ^([^.]+)$ $1.php [NC,L]
    
    
  4. elkey:

    Здравствуйте. Составил такое правило для работы поддоменов 

    RewriteCond %{HTTP_HOST} ^(.*)\.main-dom\.net$ [NC]
    RewriteCond %{REQUEST_FILENAME} !-f 
    RewriteRule ^(.*)$ http://main-dom.net/subdomain/%1 [P,L]

    в результате по адресу site1.main-dom.net показывается страница с параметрами main-dom.net/subdomain/site1 

    Как сделать, чтобы наоборот, при переходе main-dom.net/subdomain/site1 пользователь видел адрес поддомена ( site1.main-dom.net) в строке браузера? Не могу сообразить. Подскажите пожалуйста. 

    • Alexey:

      при переходе main-dom.net/subdomain/site1 пользователь видел адрес поддомена ( site1.main-dom.net) в строке браузера

      Только адрес? А что касается содержимого страницы?

      Протестировал на примере своего сайта, следующее правило

      RewriteRule subdomain/([^/]+) https://$1.hackware.ru

      при запросе страницы https://hackware.ru/subdomain/something перенаправляет на страницу https://something.hackware.ru/

      • Аноним:

        Алексей, спасибо за ответ.   У меня такое перенаправление работает, только если добавляю [L]

        RewriteRule subdomain/([^/]+) https://$1.main-dom.com [L]
        перебрасывает на поддомен, но происходит "циклическое перенаправление на странице"  (

        Показываться должна та же страница, что по адресу /subdomain/{параметр}
        Просто есть случае, когда к ней обращаются напрямую, а не через адрес поддомена.  нужно  добиться, чтобы все правила работали.

        • Alexey:

          Если честно, я не понял ваше сообщение — я не понимаю, чего вы хотите и не понимаю, в чём ваша проблема… Из ваших двух сообщений даже не понятно, вы хотите, чтобы пользователь перенаправлялся на страницу main-dom.com/subdomain/ЧТО-ТО или вы хотите чтобы пользователь перенаправлялся на subdomain.main-dom.com?

          Если возникает бесконечная переадресация — значит из-за других правил mod_rewrite.

    • Alexey:

      Теперь всё понятно. Но, к сожалению, добиться этого не удастся по следующим причинам:

      • правила RewriteRule делают преобразование относительно текущего хоста
      • в рассматриваемом задании необходимо, чтобы выполнялось переписывание правил между двух хостов, по этой причине мы используем http и https при указании пункта назначения
      • если использовать http и https, то выполняется немедленный редирект, без флагов и без возможности показать содержимое другого хоста не меняя адрес в браузере
      • то есть оба варианта обращения будут приводить к обязательным редиректам, то есть получатся циклические бесконечные редиректы.

      С одной стороны, вы добились, что правила действительно работают, но победить бесконечные редиректы не удастся.

      То есть то, что вы описали, в принципе возможно, но невозможно сделать средствами mod_rewrite.

      Можно попробовать реализовать средствами:

      • iframe в HTML (пример ниже)
      • модулем mod_alias сервера Apache (смотрите директивы alias, LocationMatch, AliasMatch)
      • каким-нибудь скриптом, который будет выполнять функции прозрачного прокси на субдоменах

      Для того, чтобы реализовать задуманное методами mod_rewrite, нужно убрать «рассинхрон», когда из папки, где фактически размещены файлы, пользователь перенаправляется на другой хост. То есть если вы разместите контент в папках субдоменов, то всё будет элементарно.

      ЛИБО перенаправляйте пользователей на подпапки вместо субдоменов, если вы не хотите/не можете перенести контент оттуда.

      Пример реализации с помощью iframe

      Для субдомена https://something.hackware.ru/ в файле index.html создаётся примерно следующее (путь https://hackware.ru/subdomain/something/ можно генерировать автоматически для каждого субдомена в PHP скрипте):

      <iframe src="https://hackware.ru/subdomain/something/?fromsubdomain=true" style="position:fixed; top:0; left:0; bottom:0; right:0; width:100%; height:100%; border:none; margin:0; padding:0; overflow:hidden; z-index:999999;">
      	Ваш браузер не поддерживает iframes
      </iframe>

      Обратите внимание, что мы добавили fromsubdomain=true в качестве строки запроса.

      В корневой папке для https://hackware.ru/ в файле .htaccess прописываем примерно следующее (при желании, можно составить правило так, чтобы сохранить параметры после ? если они нужны):

      RewriteEngine On
      RewriteCond "%{QUERY_STRING}" !=fromsubdomain=true [NC] # чтобы избежать бесконечных редиректов
      RewriteRule subdomain/([^/]+) https://$1.hackware.ru

      В результате мы получим, что при открытии https://hackware.ru/subdomain/something/ будет выполнятся переадресация на https://something.hackware.ru/, но всё равно будет показано содержимое https://hackware.ru/subdomain/something/

      При этом субдомен будет делать запрос на показа страницы вида https://hackware.ru/subdomain/something/?fromsubdomain=true и для такой страницы переадресация не будет выполняться.

      • Аноним:

        Alexey, спасибо за развернутый ответ! Хорошо прояснили ситуацию 🙂  Буду разбираться. 

  5. Добрый день, прошу помощи! Задача такая:

    1. Cтандартными средствами панели управления хостинга организовано направление поддомена poddomen.site.ru на сайт site.ru

    2. В корневой папке (public_html) сайта site.ru размещён файл poddomen.php

    3. Как реализовать вариант, чтобы при переходе пользователя по адресу poddomen.site.ru сразу на поддомене poddomen.site.ru открывалась страница poddomen.php?

    • Alexey:

      Приветствую! Без затруднений можно сделать так, чтобы при посещении poddomen.site.ru делалось перенаправление на site.ru/poddomen.php.

      Следующее правило будет работать, но будет выполнять редирект:

      RewriteEngine on
      RewriteRule .* http://site.ru/poddomen.php

      В подобной конструкции отменить редирект невозможно, поскольку poddomen.site.ru и site.ru это два разных хоста (два разных сайта).

      И тем не менее, средствами mod_rewrite есть способ сделать так, чтобы нужная страница показывалась без редиректа. Для этого нужно использовать конструкцию вида:

      RewriteEngine on
      RewriteRule .* /ПУТЬ/В/ФАЙЛОВОЙ/СИСТЕМЕ/СЕРВЕРА/poddomen.php

      Причём /ПУТЬ/В/ФАЙЛОВОЙ/СИСТЕМЕ/СЕРВЕРА — это не абсолютный путь, а относительный путь, где корнем считается директория, указанная с директивой DocumentRoot.

      На локальном сервере у меня это сработало.

      Если включён модуль mod_proxy (а он обычно НЕ включён!), то можно сделать так:

      RewriteRule .* http://site.ru/poddomen.php [P]

      Или так:

      ProxyPass .* http://site.ru/poddomen.php

      Если с модулем mod_proxy не получилось, то можно использовать модуль mod_alias, который обычно включён, но загвоздка в том, что настройку нужно делать не в файле .htaccess, а в конфигурационных файлах веб-сервера Apache в секции <Directory> или в <VirtualHost> (то есть для виртуальных хостингов этот метод не подходит):

      Alias .* /var/htdocs/www/poddomen.php

      Это примерное значение — вам нужно вписать свои данные, общий вид этой директивы:

      Alias [URL-path] file-path|directory-path

      Если не сработает, то попробуйте вместо Alias использовать ScriptAlias:

      ScriptAlias .* /var/htdocs/www/poddomen.php
      
  6. Алексей:

    Добрый день!
    Подскажите, пожалуйста. Перехожу по ссылке вида
    https://site.ru/catlog/cat/prod/
    В .htaccecc такое правило из вашего примера:
    RewriteEngine On
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule ^(.*)/$ /$1[L,R=301]
    получаю редирект на
    http://site.ru/catlog/cat/prod
    Такое поведение и задумано или мне нужно в какую-то сторону покопать?  Почему на http, а не на https?
    Спасибо!

    • Alexey:

      Приветствую! Протокол должен оставаться неизменным. Но в строке правила имеется ошибка — пропущен пробел при записи флагов. То есть правильно должно быть так:

      RewriteEngine On
      RewriteCond %{REQUEST_FILENAME} !-d
      RewriteRule ^(.*)/$ /$1 [L,R=301]
      
  7. Алексей:

    Спасибо, Алексей!

    Ваш цикл статей сильно помог. А так же инструмент отслеживания редиректов, гугл и много экспериментов дали мне такой .htaccess:

    # Remove trailing slash from non-filepath urls
    RewriteCond %{REQUEST_URI} /(.+)/$
    RewriteCond %{REQUEST_URI} !^/administrator/$
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^ https://site.ru/%1 [R=301,L]
    
    # убираем множественные слэши в урле
    RewriteCond %{REQUEST_URI} ^(.*?)\/{2,}(.*?)$
    RewriteRule . %1/%2 [L,R=301]
    RewriteCond %{THE_REQUEST} //
    RewriteRule .* https://site.ru/$0 [R=301,L]
    
    # Remove www from urls
    RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
    RewriteRule ^(.*)$ https://%1/$1 [R=301,L]

    Может быть тут и нет конкретно ваших примеров.Я компилировал.

    Сайт на джумле и на хостинге. https и sef включены. Видимо, эти два момента имеют значение. Я это вклинил в стандартный .htacess и в таком виде оно (надо ещё потестировать) вроде заработало. Без прямого указания сайта и протокола в RewriteRule, были редиректы на http. Я об этом выше писал.

    Не могли бы вы прокомментироавть человеческим языком, что происходит в секции про множественные УРЛы во втором RewriteCond и RewriteRule? В первой части я понял вроде, во второй не могу понять. Но оно работат. А без него нет.

    • Alexey:

      Приветствую! В этом фрагменте 2 правила:

      #1:

      RewriteCond %{REQUEST_URI} ^(.*?)\/{2,}(.*?)$
      RewriteRule . %1/%2 [L,R=301]

      Здесь:

      • ^ - начало строки
      • (.*?) - что угодно, причём записано странно, достаточно указать «(.*)»
      • \/{2,} - два и более слешей, причём опять записано неправильно — слеш не является специальным символом и экранировать его не надо
      • (.*?) - опять что угодно и опять записано неправильно
      • $ - конец строки
      • %1 — обратная ссылка на то, что найдено в первых скобках
      • / - буквальный слеш
      • %2 — обратная ссылка на то, что найдено во вторых скобках

      Я потестировал, не похоже, что это работает.

      #2:

      RewriteCond %{THE_REQUEST} //
      RewriteRule .* https://site.ru/$0 [R=301,L]

      Это правило или я не понимаю, или там бессмыслица.

      UPD, хотя понял, здесь $0 — всегда пусто, поэтому если в строке запроса найдены два слеша подряд, то просто делается перенаправление на https://site.ru/, при этом содержимое строки запроса теряется.

      В этой статье я проверил и улучшил правила по удалению лишних слешей в начале и конце запроса, также добавил правило по удалению лишних слешей в середине запроса: https://hackware.ru/?p=5645#5314. Рекомендую попробовать их.

  8. Mitrios:

    RewriteEngine on
    RewriteCond %{HTTPS} !on
    RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI}

    RewriteEngine on
    RewriteCond %{HTTPS} off
    RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI}

    Обе эти конструкции валят сайт и вызывают ошибку 500. Онлайн тестеры htaccess пишут что эти выражения не работают

    • Alexey:

      Вы написали комментарий на сайт, в файле .htaccess которого используется одна из этих конструкций.

      1. Другие правила mod_rewrite не вызывают ошибок? (это проверка на то, что модуль mod_rewrite вообще работает).
      2. Если просто открыть сайт по протоколу HTTPS не появляется ошибки 500? (это проверка, что правильно настроена работа с протоколом HTTPS).

      А обе эти конструкции работают как надо, ваши проблемы в чём-то другом.

    • Alexey:

      Вот вам даже мнение двух онлайн тестеров .htaccess — всё работает, происходит редирект на HTTPS версию.

  9. Аноним:

    RewriteEngine on

    RewriteRule "(.+)" "https://НОВЫЙ-САЙТ.ru/$1" [R=301,L]

    RewriteRule ^ "https://НОВЫЙ-САЙТ.ru/" [R=301,L]

    Почему в этом правиле вы (.+) берете в кавычки, а ^ не берете?
    Как знать когда использовать кавычки, я не нашел этого в материалах, потому что если взять символ начала строки в кавички «^» то правило тоже сработает?

    • Alexey:

      Кавычки нужны только для строк, в которых содержаться пробелы. Во всех остальных случаях использование и неиспользование кавычек ни на что не влияет.

      Здесь кавычки используются скорее для визуальной группировки.

  10. Аноним:

    Добрый день!

    В правиле редиректа HTTP на HTTPS

    RewriteEngine on

    RewriteCond %{HTTPS} !on

    RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI}

    в RewriteRule флаг QSA автоматически включен, т.е. почему в браузере автоматически подставляется %{QUERY_STRING} хотя мы ее явно не указываем?

    и как здесь с флагом L быть его надо указывать если я хочу остановить дальнейшее рассмотрение набора rewrite-правил в первом цикле, когда понятно что HTTPS не используется?
    Благодарю.

    • Alexey:

      в RewriteRule флаг QSA автоматически включен, т.е. почему в браузере автоматически подставляется %{QUERY_STRING} хотя мы ее явно не указываем?

      %{QUERY_STRING} — это переменная среды. И она вставляется потому что мы её явно указываем в правиле, которое вы привели.

      и как здесь с флагом L быть его надо указывать если я хочу остановить дальнейшее рассмотрение набора rewrite-правил в первом цикле, когда понятно что HTTPS не используется?

      Если в качестве цели переадресации указан сайт вместе с протоколом (HTTP или HTTPS), то о флаге [L] можно больше не беспокоиться, поскольку автоматически подразумевается флаг [R], а это означает немедленную переадресацию, без дальнейшего рассмотрения правил.

      • Аноним:

        %{QUERY_STRING} — это переменная среды. И она вставляется потому что мы её явно указываем в правиле, которое вы привели.

         но ведь мы явно указали только:

        %{HTTP_HOST} - domain.com

        и

        %{REQUEST_URI} - /somepage.php

        а набор

        %{QUERY_STRING} там не указан

        хотя при добавлении к ссылки ?a=1&b=b&auto эти параметры  будут подставлены в переадресацию

        • Alexey:

          Пардон за невнимательность. Если в новом адресе в mod_rewrite не указана строка запроса, то она берётся из присланного и добавляется к конечному адресу. Если в правиле указана новая строка запроса, то она автоматически перезаписывается на указанный в правиле mod_rewrite.

          Если указан флаг [QSA], то строки в новом адресе и присланный запрос объединяются.

          Если указан [QSD], то присланная строка запроса будет отброшена.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *