Почему .htaccess не работает: 500, 403, 404, бесконечный редирект — и как чинить

Самые частые жалобы на .htaccess: «после правки сайт отдаёт 500», «403 Forbidden», «включил ЧПУ — всё стало 404», «бесконечный редирект», «правлю файл, а ничего не меняется». Ниже — для каждого симптома список причин и что конкретно делать. Почти всегда первый шаг один и тот же: открыть error.log сервера — там обычно точная строка и причина. С этого и начнём — см. раздел про диагностику, а в конце — чеклист по порядку.

Важно: .htaccess работает только на Apache (и на LiteSpeed Enterprise, который его эмулирует). На nginx, Caddy, OpenLiteSpeed файл .htaccess не читается вообще — там настройки задаются в конфиге сервера. Если вы не уверены, какой у вас сервер, — это первое, что стоит выяснить (см. раздел «.htaccess вообще не действует»).

1. Первым делом: как увидеть, что не так

90% проблем с .htaccess диагностируются за минуту, если знать, куда смотреть.

  • Откройте error.log — там почти всегда точная строка файла и причина. Где он: в cPanel — «Metrics → Errors» или файл ~/logs/; в ISPmanager — раздел «Журналы»; в Plesk — «Logs» сайта; на VPS / выделенном/var/log/apache2/error.log (Debian / Ubuntu) или /var/log/httpd/error_log (CentOS / RHEL), для конкретного сайта — путь из директивы ErrorLog в его vhost.
  • Как читать запись: ищите имя своего .htaccess и сообщения вида Invalid command 'php_flag', perhaps misspelled or defined by a module not included in the server configuration, … not allowed here, client denied by server configuration, Request exceeded the limit of 10 internal redirects.
  • Метод бинарного поиска: закомментируйте (# в начале строк) половину .htaccess, обновите страницу; если ошибка пропала — проблема в закомментированной половине, делите дальше.
  • Отладка rewrite: LogLevel alert rewrite:trace3 печатает каждый шаг mod_rewrite в error.log — но это директива конфига сервера / vhost, в .htaccess не работает; на хостинге используйте тестер /rewrite/ (пошаговая трассировка правил).
  • Инструменты: линтер /check/ — находит ошибки синтаксиса, неподдерживаемые / устаревшие директивы, php_value на FPM, опечатки в именах; тестер /rewrite/ — что матчится и куда переписывается на конкретном URL.

Подробный гид по отладке — статья «Отладка .htaccess: error_log, rewrite:trace».

2. 500 Internal Server Error

Сайт или один раздел отдаёт «500 Internal Server Error» сразу после добавления или правки .htaccess. Почти всегда — ошибка в самом файле.

Причины:

  1. Синтаксическая ошибка или опечатка в директиве: лишняя кавычка, неизвестное слово, незакрытый <IfModule> / <FilesMatch>.
  2. Неподдерживаемая директива: php_value / php_flag на PHP-FPM / FastCGI (Invalid command 'php_flag'); Options … без разрешённого AllowOverride Options (Options not allowed here) (подробнее — статья «AllowOverride…»); Order / Allow / Deny на Apache 2.4 без модуля mod_access_compat.
  3. Модуль не загружен: RewriteEngine без mod_rewrite, Header … без mod_headers, ExpiresByType без mod_expires — лечится обёрткой <IfModule mod_xxx.c> … </IfModule>.
  4. Кодировка или перевод строк: BOM в начале файла, не-UTF-8, иногда CRLF — пересохраните файл как UTF-8 без BOM с переводами строк LF.
  5. Бесконечная внутренняя рекурсия rewrite-правил (Request exceeded the limit of 10 internal redirects) — обычно правило, переписывающее URL само на себя без условия-страховки.

Как чинить:

  • Первым делом — error.log: там точная строка и причина (см. раздел 1).
  • Прогнать файл через линтер /check/.
  • Обернуть «рискованные» директивы в <IfModule>.
  • Настройки PHP на FPM — через .user.ini в корне сайта или панель хостинга (подробнее — /performance/ и /security/ → hardening PHP).
  • Если ничего не помогает — закомментировать весь файл и возвращать строки по частям.
.htaccessкопировать
# вызовет 500 на PHP-FPM / FastCGI:
php_flag display_errors Off

# безопаснее (только для mod_php), не уронит сервер на FPM:
<IfModule mod_php.c>
    php_flag display_errors Off
</IfModule>
# на PHP-FPM настройки PHP — в .user.ini в корне сайта:
#   display_errors = Off

3. 403 Forbidden

Сервер отдаёт «403 Forbidden / You don't have permission to access …» — доступ запрещён где-то в конфигурации.

Причины:

  1. Require all denied / Deny from all в этом или родительском .htaccess (либо в конфиге сервера) — закрыли больше, чем хотели.
  2. Options -Indexes и в каталоге нет index.php / index.html — запросили сам каталог.
  3. Права доступа: каталог не 755, файл не 644, либо неверный владелец / группа (после загрузки по FTP или распаковки архива).
  4. <FilesMatch> / <Files> со слишком широким regex зацепил нужные файлы.
  5. Require ip … / IP-фильтр не включает ваш текущий адрес (особенно за прокси или мобильным интернетом).
  6. WAF / mod_security хостинга заблокировал запрос — тогда в error.log будет ModSecurity: Access denied.

Как чинить:

  • Просмотреть все .htaccess от корня document root до нужного файла на предмет denied / Deny.
  • error.log: client denied by server configuration, AH01797, ModSecurity.
  • Проверить и выставить права (755 на каталоги, 644 на файлы) через FTP / SSH.
  • Сузить regex в <FilesMatch>; линтер /check/ подсвечивает подозрительно широкие маски.
  • Правильно настроить доступ по IP / паролю — центр безопасности.

4. 404 после включения ЧПУ (mod_rewrite)

Добавили правила «человекопонятных URL» — и теперь все такие адреса отдают 404, хотя главная работает.

Причины:

  1. Есть RewriteRule, но нет RewriteEngine On (или он стоит ниже правил) — все правила игнорируются.
  2. mod_rewrite не включён на хостинге.
  3. Неправильный RewriteBase — сайт в подкаталоге, а база не указана или указана неверно.
  4. Фронт-контроллер-правило без проверки «файл / каталог не существует» (!-f / !-d) — переписывает в index.php в том числе существующие ассеты.
  5. Options +MultiViews (mod_negotiation) перехватывает запрос раньше mod_rewrite — выключите: Options -MultiViews.
  6. Файл или маршрут, на который переписываем, действительно не существует (опечатка в подстановке).

Как чинить:

  • RewriteEngine On — первой строкой блока rewrite.
  • Проверить модуль: apachectl -M | grep rewrite, либо phpinfo()apache2handlerLoaded Modules.
  • В подкаталоге добавить RewriteBase /подкаталог/.
  • Использовать типовой безопасный фронт-контроллер (см. блок ниже).
  • Проверить правила на конкретном URL — тестер /rewrite/; как устроен mod_rewrite — туториал «Как работает mod_rewrite».
.htaccessкопировать
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . index.php [L]

WP-специфика ЧПУ (блок # BEGIN WordPress) — хаб «.htaccess для WordPress».

Фреймворк-специфика ЧПУ (Laravel/Symfony/Joomla/…) — хаб «.htaccess для CMS и фреймворков».

5. Бесконечный редирект (ERR_TOO_MANY_REDIRECTS)

Браузер пишет «слишком много перенаправлений» / в error.logRequest exceeded the limit of 10 internal redirects. Правило заворачивает запрос по кругу.

Причины:

  1. HTTPS-редирект за прокси / CDN: до Apache доходит уже расшифрованный трафик, %{HTTPS} всегда off, правило бесконечно редиректит «на https» — нужно проверять %{HTTP:X-Forwarded-Proto}.
  2. Два конфликтующих правила www↔без-www (или .htaccess + настройка CMS / панели): одно ведёт на www, другое — обратно.
  3. RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L] без условия — срабатывает, даже когда уже https.
  4. DirectoryIndex / ErrorDocument, указывающий сам на себя или на несуществующий обработчик.
  5. Правило, добавляющее / убирающее завершающий /, без условия — добавляет и тут же убирает.

Как чинить:

  • В DevTools (вкладка Network) посмотреть цепочку 301 / 302 — видно, кто на кого ведёт.
  • Проверить реальную цепочку для конкретного URL — /redirect-check/.
  • Добавить условие-страховку, чтобы правило не срабатывало повторно (см. блок ниже).
  • За CDN / прокси проверять %{HTTP:X-Forwarded-Proto}, а не %{HTTPS}.
  • Убедиться, что одно и то же перенаправление не настроено и в .htaccess, и в CMS / панели одновременно.
.htaccessкопировать
RewriteEngine On
# редирект на HTTPS, который работает и за CDN/прокси
RewriteCond %{HTTP:X-Forwarded-Proto} !https
RewriteCond %{HTTPS} off
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]

6. .htaccess вообще не действует

Правки в .htaccess не дают никакого эффекта — ни ошибки, ни нового поведения. Скорее всего файл просто не читается.

Причины:

  1. Сервер — не Apache. На nginx, Caddy, OpenLiteSpeed .htaccess не читается в принципе — настройки делаются в конфиге сервера. (LiteSpeed Enterprise — читает; nginx + «эмуляция .htaccess» в некоторых панелях — частично.)
  2. AllowOverride None в конфиге сервера / vhost — на VPS это частый дефолт; .htaccess есть, но Apache его не применяет.
  3. Файл не в том каталоге — нужен в корне document root (или в нужной папке), а лежит, например, на уровень выше.
  4. Файл назван неверно: htaccess.txt, .htaccess.txt, htaccess, или сохранён не как обычный текст. Windows по умолчанию прячет расширения — .htaccess легко оказывается .htaccess.txt.
  5. Ответ закэширован прокси / CDN — вы видите старую версию страницы, а не результат нового .htaccess.

Если у вас nginx — перевести правила .htaccess в nginx-конфиг поможет конвертер .htaccess → nginx (примерный перевод — проверяйте вручную).

Подробно про разные хостинги и панели (cPanel, ISPmanager, Plesk, Beget, LiteSpeed, nginx-only) — хаб «.htaccess на разных хостингах».

Как проверить:

  • Добавьте первой строкой .htaccess заведомо неверную команду — например ESJDKLKJ — и обновите страницу. Появилась 500 Internal Server Error → файл читается (проблема в другом разделе этого гида). Не появилась → файл не читается, причины 1–4 выше.
  • Уточнить у хостинга тип сервера и значение AllowOverride.
  • Проверить имя и расположение файла по FTP (включить показ расширений).
  • Сбросить кэш CDN. Прогнать файл через линтер /check/.

7. Конкретное RewriteRule не срабатывает (а другие — да)

Одно правило mod_rewrite не применяется, хотя RewriteEngine On стоит и соседние правила работают.

Причины:

  1. Паттерн не матчит из-за ведущего /: в .htaccess путь приходит без начального слэша (about/), а в <Directory> / vhost — с (/about/); пишите ^about/ или ^/?about/.
  2. Предыдущее правило с [L] (или [END]) уже остановило обработку — до этого правила запрос не дошёл.
  3. RewriteCond действует только на самый ближайший следующий RewriteRule; между ними нельзя вставлять другие RewriteRule.
  4. Не экранированы спецсимволы regex: . — это «любой символ», ? / + / ( тоже метасимволы; пишите \., \?.
  5. Query-string в паттерн RewriteRule не входит?id=5 нельзя матчить в RewriteRule, нужен RewriteCond %{QUERY_STRING} … (и флаг [QSA], чтобы старый query не потерялся).
  6. Относительная подстановка без RewriteBase в .htaccess подкаталога — Apache не знает префикс.
  7. Регистр не совпадает, а флага [NC] нет.

Как чинить:

  • Прогнать правило в тестере /rewrite/ — он показывает пошагово, что матчится и почему.
  • Убрать промежуточные RewriteRule между RewriteCond и нужным правилом.
  • Проверить, нет ли [L] в правилах выше.
  • Справка по синтаксису regex и флагов — туториал «Как работает mod_rewrite» и справочник директив mod_rewrite.

8. Чеклист: .htaccess не работает — проверь по порядку

  1. Сервер точно Apache (или LiteSpeed Enterprise)? На nginx .htaccess не работает. — раздел 6
  2. Файл реально называется .htaccess (без .txt) и лежит в нужном каталоге? — раздел 6
  3. Хостинг разрешает .htaccess? Добавь ESJDKLKJ первой строкой — должна появиться 500; если нет — .htaccess не читается. — раздел 6
  4. Что в error.log? Там почти всегда точная строка и причина. — раздел 1
  5. Если 500 — какая директива виновата? Оберни в <IfModule> или убери; php_value / php_flag на FPM не работает. — раздел 2, /check/
  6. Если ЧПУ дают 404 — есть RewriteEngine On первой строкой? Включён mod_rewrite? — раздел 4
  7. Если бесконечный редирект — сайт за CDN / прокси? Проверь %{HTTP:X-Forwarded-Proto}, а не %{HTTPS}. — раздел 5
  8. Прогони файл через линтер /check/, а правила mod_rewrite — через тестер /rewrite/. — раздел 1