Академия mod_rewrite. Урок 5: собираем .htaccess целиком и не ломаем его

Заключительный урок курса: собираем реальный .htaccess — правильный порядок правил, защита от петель редиректов, фронт-контроллер, полный пример с комментариями и пошаговая отладка, когда что-то пошло не так.

Урок 5 из 5 · ← предыдущий · оглавление курса

Порядок правил критичен

Apache обрабатывает правила сверху вниз. Неправильный порядок — самая частая причина «что-то работает не так».

Рекомендуемый порядок в .htaccess:

  1. Канонизация протокола — http → https (редирект).
  2. Канонизация хоста — www ↔ без-www (редирект).
  3. Конкретные редиректы — старые URL → новые (301).
  4. Перезаписи — фронт-контроллер и ЧПУ-правила.

Более конкретные правила — раньше общих. Правило для конкретной страницы должно стоять выше 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.

.htaccessкопировать
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:

.htaccessкопировать
<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 отключён или не установлен.

Отладка: что делать, когда не работает

Пошаговый алгоритм:

  1. Проверить синтаксис в линтере .htaccess — ошибки синтаксиса дают 500, и всё остальное не имеет смысла.
  2. Убедиться, что .htaccess читается: добавьте первой строкой явный мусор — ZZZERROR. Должен появиться 500. Если нет — Apache не читает этот файл (AllowOverride None, файл не там или не так назван).
  3. Трассировать правила в тестере RewriteRule — вставьте блок и тестовый URL, посмотрите пошаговый разбор.
  4. Прочитать error.log сервера — точная строка и причина ошибки.
  5. Построчный разбор в объяснялке .htaccess.
  6. База ошибок — симптом → причина → исправление в гиде «Почему .htaccess не работает».

Частые ошибки

  • Ведущий / в паттерне RewriteRule в контексте .htaccess — лишний: путь подаётся без ведущего слэша. В конфиге Apache (vhost) — с ведущим слэшем.
  • [L] выше нужного правила — останавливает обработку до того, как нижние правила успевают сработать.
  • RewriteCond «прилипает» только к одному RewriteRule — одно условие не распространяется на несколько правил ниже.
  • Забытое экранирование . — паттерн ловит лишнее (см. урок 4).
  • Query string не в RewriteRule — паттерн не видит ?param=value. Проверяйте строку запроса через RewriteCond %{QUERY_STRING}.
  • Нет RewriteEngine On — все правила молча игнорируются.

Попробуй сам

Соберите свой блок правил в генераторе .htaccess, проверьте синтаксис в линтере, затем протестируйте в тестере RewriteRule. Это самый быстрый способ убедиться, что всё работает как задумано. Или всё сразу — песочница .htaccess.

Упражнения

  1. Соберите .htaccess с https-редиректом + www-канонизацией (без www) + фронт-контроллером в правильном порядке.
    Подсказка: порядок — https → www → фронт-контроллер. Не забудьте !-f/!-d перед фронт-контроллером.
    Проверьте в тестере RewriteRule.
  2. Найдите ошибку в этом блоке:
    .htaccess с ошибкойкопировать
    RewriteEngine On
    RewriteCond %{HTTPS} off
    RewriteRule ^ http://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
    Подсказка: редирект с http на http — бесконечная петля.
  3. Почему этот фронт-контроллер «съел» все картинки?
    .htaccess с проблемойкопировать
    RewriteEngine On
    RewriteRule ^ index.php [L]
    Подсказка: нет проверок !-f и !-d, поэтому все запросы — в том числе к существующим файлам — уходят в index.php.

Куда идти дальше

Поздравляем с окончанием курса! Вы прошли путь от первого правила до сборки целого .htaccess. Что дальше: