PCRE: регулярные выражения для mod_rewrite
В .htaccess регулярными выражениями (PCRE — тот же движок, что в PHP preg_*, но без ограничителей /…/ и почти без модификаторов) сопоставляется первый аргумент RewriteRule, второй аргумент RewriteCond, а также RedirectMatch, <FilesMatch>, SetEnvIf, Header edit. Регистронезависимость включается не модификатором (?i), а флагом [NC]. Как работает сам mod_rewrite (правила, переменные, флаги) — туториал «Как работает mod_rewrite»; прогнать свой паттерн на конкретном URL — тестер /rewrite/.
1. Зачем regex в .htaccess
Понимая регулярки, перестаёшь писать правила «методом тыка». Где именно в .htaccess встречается regex:
RewriteRule— первый аргумент (паттерн пути):RewriteRule ^post/([0-9]+)$ /post.php?id=$1 [L].RewriteCond— второй аргумент, если он не начинается с</>/=/-:RewriteCond %{HTTP_HOST} ^www\.example\.com$ [NC].RedirectMatch(mod_alias) —RedirectMatch 301 ^/old/(.*)$ /new/$1.<FilesMatch>/<DirectoryMatch>/<LocationMatch>—<FilesMatch "\.(sql|bak|log)$">.SetEnvIf/SetEnvIfNoCase—SetEnvIfNoCase User-Agent "(nikto|sqlmap)" bad_bot.
2. Якоря и границы
^— начало строки.$— конец строки.\b— граница слова (буква↔не-буква); удобно в фильтрах поUser-Agent/Referer.
Важно: в RewriteRule тестируемая строка — это путь запроса без ведущего / (когда правило в .htaccess), поэтому пишут ^about$, а не ^/about$. В RewriteCond со значением %{REQUEST_URI} слэш, наоборот, присутствует. Примеры: ^$ (главная страница), ^index\.php$ (точно index.php), \.php$ (заканчивается на .php).
3. Символьные классы
[abc]— один из перечисленных символов;[^abc]— любой, кроме перечисленных.- Диапазоны:
[a-z],[0-9],[A-Za-z0-9_-]. [^/]— любой символ, кроме слэша (частый приём «ровно один сегмент пути»).- Сокращения:
\d=[0-9],\w=[A-Za-z0-9_],\s= пробельные;\D/\W/\S— наоборот. .— любой символ, кроме перевода строки (вне класса! внутри[ ]точка — это просто точка).- Грабли: дефис
-в классе ставь первым или последним ([-a-z],[a-z-]) либо экранируй — иначе это диапазон.
Примеры: ^[a-z0-9-]+$ (slug), ^[^/]+/[^/]+$ (ровно два сегмента пути).
4. Кванторы
*— 0 или больше;+— 1 или больше;?— 0 или 1 (необязательно).{3}— ровно 3;{2,}— 2 и больше;{2,4}— от 2 до 4.- По умолчанию кванторы жадные — берут как можно больше; ленивые
*?+???— как можно меньше (нужны редко — «до первого/» обычно делают через[^/]+).
Примеры: .* (что угодно, в т.ч. пусто), .+ (что угодно непустое), [^/]+ (один непустой сегмент), [0-9]{1,6} (число до 6 цифр), /?$ (необязательный завершающий слэш).
5. Группы и захваты
( … ) — группа с захватом; куски доступны в подстановке RewriteRule как $1, $2, … (нумерация по открывающей скобке слева направо), а от последнего сработавшего RewriteCond — как %1, %2, …. (?: … ) — группировка без захвата (когда скобки нужны только для квантора или альтернативы и не хочется сбивать нумерацию $N). Альтернатива внутри группы: (jpe?g|png|gif|webp).
- Вложенные группы нумеруются по открывающим скобкам:
^((a)(b))$даёт$1=ab,$2=a,$3=b. $0вRewriteRule— вся совпавшая строка.- Не нужен захват — пиши
(?:…), чтобы не сбивать номера$N.
# /post/123 -> /post.php?id=123 RewriteRule ^post/([0-9]+)$ /post.php?id=$1 [L] # /ru/about -> /about?lang=ru RewriteRule ^([a-z]{2})/(.*)$ /$2?lang=$1 [L,QSA]
# ?id=42 -> захват из query-строки через RewriteCond RewriteCond %{QUERY_STRING} (^|&)id=([0-9]+)(&|$) RewriteRule ^item$ /item.php?id=%2 [L]
6. Экранирование
Метасимволы . ^ $ * + ? ( ) [ ] { } | \ имеют спецзначение; чтобы матчить их буквально, ставь \ перед символом.
\.— точка (самое частое:\.html$,^www\.example\.com$,\.(jpg|png)$).\?— буквальный вопрос (но вRewriteRulequery-строки нет — см. раздел 7).\/— слэш (в PCRE без ограничителей экранировать не обязательно, но многие пишут\/по привычке — это не ошибка).\\— обратный слэш.- Грабли: причина №1 «правило ловит лишнее» — забытый
\перед.:^index.php$совпадёт и сindexXphp; пишите^index\.php$. В подстановкеRewriteRule(правая часть) regex-экранирование не нужно — там обычный текст с$N/%N.
7. Особенности в RewriteCond и переменных %{…}
- Тестовая строка
RewriteCond— это подставленное значение переменной:%{HTTP_HOST},%{REQUEST_URI},%{QUERY_STRING},%{HTTP_USER_AGENT},%{HTTP_REFERER},%{HTTPS}и т.д. (полный список — туториал «Как работает mod_rewrite»); regex применяется уже к этому значению. - Query-string матчат только через
RewriteCond %{QUERY_STRING} …— в паттернRewriteRulequery не попадает; для сохранения исходного query при подстановке — флаг[QSA]. RewriteCondподдерживает не только regex:-f/-d/-s/-l(файл / каталог / непустой файл / симлинк существует),</>/=/<=/>=(лексикографическое сравнение строк),expr=…(выражения; онлайн-тестер их не эмулирует).!перед паттерном — отрицание (!^www\.);[NC]— без учёта регистра;[OR]— логическое ИЛИ со следующимRewriteCond(иначе подряд идущие условия — это И).
# редирект на без-www, работает регистронезависимо RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC] RewriteRule ^ https://%1%{REQUEST_URI} [R=301,L]
8. Модификаторы PCRE: что есть и чего нет
- Есть: регистронезависимость — но не модификатором
(?i)и не суффиксом/i, а флагом[NC]уRewriteRule/RewriteCond(иSetEnvIfNoCaseвместоSetEnvIf). - Нет (по умолчанию не нужны):
/m(строка и так одна),/s(.и так не встречает\n),/x(расширенный режим),/u(Unicode /\p{…}) — кириллицу и не-ASCII в путях обычно матчат как%D0%…-последовательности или просто[^/]+. - Поддерживается, но в
.htaccessредко:(?:…)(без захвата — используется),(?i)(инлайн-флаг — работает, но привычнее[NC]),\b(граница слова), lookahead(?=…)/(?!…), lookbehind(?<=…)/(?<!…), обратные ссылки\1внутри паттерна — PCRE-движок Apache всё это умеет.
# всё, что НЕ начинается с api/ — переписываем во фронт-контроллер RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(?!api/).*$ index.php [L]
9. Готовые шаблоны
^$— главная страница (пустой путь).^([^/]+)/?$— один сегмент пути, опц. завершающий слэш — страница верхнего уровня.^([a-z0-9-]+)/([a-z0-9-]+)/?$—категория/товариз slug-ов →$1,$2.^([0-9]+)$/([0-9]+)$— числовой ID (в начале / в конце).\.(jpe?g|png|gif|webp|svg)$[NC]— файлы-картинки (анти-хотлинк, кэш).^(en|ru|de)/(.*)$— языковой префикс + остаток пути →$2?lang=$1.^(.*[^/])$→ подстановка$1/— добавить завершающий слэш, если его нет.^assets/.*\.(css|js)$— статика в/assets/(исключить из rewrite, поставить кэш).^(?!api/).*$— всё, кромеapi/…(lookahead).^old-section/(.*)$→/new-section/$1— перенос целого раздела (301).
Любой из этих паттернов прогоните на конкретном URL в тестере RewriteRule или в тестере регулярных выражений — увидите, что попало в $1/$2, и разбор паттерна. Готовые целые блоки — в рецептах и генераторе.
10. Проверить и почитать дальше
- Тестер регулярных выражений — прогнать паттерн на примерах строк, увидеть захваты
$1/$2, разбор паттерна. - Тестер RewriteRule — прогнать паттерн на URL, видно захваты
$1/%1. - «Как работает mod_rewrite» — синтаксис правил, переменные
%{…}, флаги, порядок обработки. - Справочник директив mod_rewrite.
- Рецепты .htaccess — готовые блоки под типовые задачи.
- Линтер .htaccess — находит невалидный regex и опечатки.
- «Почему .htaccess не работает» — раздел про правило, которое не срабатывает или ловит лишнее.