Академия mod_rewrite. Урок 5: собираем .htaccess целиком и не ломаем его
Заключительный урок курса: собираем реальный .htaccess — правильный порядок правил, защита от петель редиректов, фронт-контроллер, полный пример с комментариями и пошаговая отладка, когда что-то пошло не так.
Урок 5 из 5 · ← предыдущий · оглавление курса
Порядок правил критичен
Apache обрабатывает правила сверху вниз. Неправильный порядок — самая частая причина «что-то работает не так».
Рекомендуемый порядок в .htaccess:
- Канонизация протокола — http → https (редирект).
- Канонизация хоста — www ↔ без-www (редирект).
- Конкретные редиректы — старые URL → новые (301).
- Перезаписи — фронт-контроллер и ЧПУ-правила.
Более конкретные правила — раньше общих. Правило для конкретной страницы должно стоять выше catch-all паттерна. Ставьте [L] там, где обработка должна остановиться, чтобы правила не «провалились» дальше.
Защита от петель редиректов
Петля редиректа (ERR_TOO_MANY_REDIRECTS) — главная боль при работе с mod_rewrite. Частые причины:
- HTTPS за прокси/CDN:
%{HTTPS}всегдаoff, даже когда посетитель уже на HTTPS. В результате редирект https → https → https. Решение: вместо%{HTTPS}проверять%{HTTP:X-Forwarded-Proto}. - Правило переписывает в само себя: редирект с
/pageна/page/, и наоборот — с/page/обратно. Проверьте, что цель редиректа не попадает под то же правило. - Фронт-контроллер без проверки файла: без
!-f/!-dвсе запросы (включая CSS и картинки) идут вindex.php, который может снова редиректить. - Проверка через
%{THE_REQUEST}:%{REQUEST_URI}меняется при перезаписях, а%{THE_REQUEST}— нет. Используйте его, если нужно проверить оригинальный URL запроса, а не уже переписанный.
Фронт-контроллер: index.php для всего
Стандартный шаблон для PHP-фреймворков и CMS: все запросы к несуществующим файлам и каталогам отдавать в index.php.
RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^ index.php [L,QSA]
Почему !-f и !-d обязательны: без них CSS, JS, картинки и другие статические файлы тоже уйдут в index.php — сайт «потеряет» стили и изображения. Условия !-f (не файл) и !-d (не каталог) пропускают через фронт-контроллер только маршруты приложения.
Паттерн ^ (или просто .) матчит любой путь. [QSA] — сохранить строку запроса.
Полный пример .htaccess
Реалистичный .htaccess для PHP-сайта на хостинге с Apache:
<IfModule mod_rewrite.c> RewriteEngine On # 1. HTTPS-редирект (за прокси/CDN — через X-Forwarded-Proto) RewriteCond %{HTTP:X-Forwarded-Proto} =http [OR] RewriteCond %{HTTPS} off RewriteCond %{HTTP:X-Forwarded-Proto} !https RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L] # 2. www → без-www (или наоборот — выберите одно) RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC] RewriteRule ^ https://%1%{REQUEST_URI} [R=301,L] # 3. Конкретные 301-редиректы (старые URL) RewriteRule ^about\.html$ /about [R=301,L] RewriteRule ^contacts\.html$ /contacts [R=301,L] # 4. Фронт-контроллер RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^ index.php [L,QSA] </IfModule>
Обёртка <IfModule mod_rewrite.c> предотвращает ошибки 500 на серверах, где mod_rewrite отключён или не установлен.
Отладка: что делать, когда не работает
Пошаговый алгоритм:
- Проверить синтаксис в линтере .htaccess — ошибки синтаксиса дают 500, и всё остальное не имеет смысла.
- Убедиться, что .htaccess читается: добавьте первой строкой явный мусор —
ZZZERROR. Должен появиться 500. Если нет — Apache не читает этот файл (AllowOverride None, файл не там или не так назван). - Трассировать правила в тестере RewriteRule — вставьте блок и тестовый URL, посмотрите пошаговый разбор.
- Прочитать error.log сервера — точная строка и причина ошибки.
- Построчный разбор в объяснялке .htaccess.
- База ошибок — симптом → причина → исправление в гиде «Почему .htaccess не работает».
Частые ошибки
- Ведущий
/в паттернеRewriteRuleв контексте.htaccess— лишний: путь подаётся без ведущего слэша. В конфиге Apache (vhost) — с ведущим слэшем. [L]выше нужного правила — останавливает обработку до того, как нижние правила успевают сработать.RewriteCond«прилипает» только к одномуRewriteRule— одно условие не распространяется на несколько правил ниже.- Забытое экранирование
.— паттерн ловит лишнее (см. урок 4). - Query string не в
RewriteRule— паттерн не видит?param=value. Проверяйте строку запроса черезRewriteCond %{QUERY_STRING}. - Нет
RewriteEngine On— все правила молча игнорируются.
Попробуй сам
Соберите свой блок правил в генераторе .htaccess, проверьте синтаксис в линтере, затем протестируйте в тестере RewriteRule. Это самый быстрый способ убедиться, что всё работает как задумано. Или всё сразу — песочница .htaccess.
Упражнения
-
Соберите
.htaccessс https-редиректом + www-канонизацией (без www) + фронт-контроллером в правильном порядке.
Подсказка: порядок — https → www → фронт-контроллер. Не забудьте!-f/!-dперед фронт-контроллером.
Проверьте в тестере RewriteRule. -
Найдите ошибку в этом блоке:
Подсказка: редирект с http на http — бесконечная петля.
RewriteEngine On RewriteCond %{HTTPS} off RewriteRule ^ http://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
-
Почему этот фронт-контроллер «съел» все картинки?
Подсказка: нет проверок
RewriteEngine On RewriteRule ^ index.php [L]
!-fи!-d, поэтому все запросы — в том числе к существующим файлам — уходят вindex.php.
Куда идти дальше
Поздравляем с окончанием курса! Вы прошли путь от первого правила до сборки целого .htaccess. Что дальше:
- Рецепты .htaccess — готовые блоки под типовые задачи с объяснениями.
- Справочник директив — синтаксис и примеры каждой директивы.
- PCRE: регулярные выражения для mod_rewrite — углублённый справочник по regex.
- SEO и редиректы через .htaccess — канонизация, 301 vs 302, перенос доменов.
- Оглавление курса — вернуться к началу.