Создание правил в .htaccess

Сначала проверьте сервер

Одна и та же строка .htaccess на одном хостинге работает, а на другом отвечает 500 на всех страницах. Прежде чем писать правило, выясните, какой сервер читает файл, как запущен PHP и где заканчивается TLS. На это отвечает несколько коротких команд. Инструменты здесь: curl, SSH, grep.

Заголовки ответа называют сервер, но не режим PHP:

curl -sSI https://example.com/

HTTP/2 200 content-type: text/html; charset=UTF-8 server: Apache

Файл-зонд в корне сайта печатает режим PHP и переменные TLS. Дайте ему случайное имя и удалите после проверки:

vi ~/example.com/probe-7f3a.php

<?php echo "HTTPS=", var_export($_SERVER['HTTPS'] ?? null, true), " XFP=", var_export($_SERVER['HTTP_X_FORWARDED_PROTO'] ?? null, true), " SERVER_SOFTWARE=", $_SERVER['SERVER_SOFTWARE'] ?? '', " sapi=", PHP_SAPI, "\n";

curl -sS https://example.com/probe-7f3a.php

На FastCGI-хостинге (DreamHost) зонд отвечает:

HTTPS='on' XFP=NULL SERVER_SOFTWARE=Apache sapi=cgi-fcgi

На своём сервере посмотрите модули Apache. apache2ctl -M нужен root или sudo и сервер, которым вы управляете; общий хостинг такую команду обычно не даёт. Это локальный Docker php:8.5-apache с mod_php:

apache2ctl -M | grep -i -E 'php|rewrite|alias|headers|version'

version_module (static) alias_module (shared) headers_module (shared) php_module (shared) rewrite_module (shared)

На FastCGI-хостинге (DreamHost) PHP работает отдельным процессом:

pgrep -l -u user php

2745746 php85.cgi

Бинарник Apache тоже там:

ls /usr/sbin/apache2*

/usr/sbin/apache2 /usr/sbin/apache2ctl

Когда правило всё-таки сломало сайт, журнал ошибок называет файл и директиву:

tail -n 1 ~/logs/example.com/https/error.log

[Fri Nov 06 23:29:54 2026] [example.com] [alert] [client 203.0.113.7:37118] [pid 3064457] config.c(2045): /home/user/example.com/.htaccess: Invalid command 'php_flag', perhaps misspelled or defined by a module not included in the server configuration

sapi=cgi-fcgi вместе с php85.cgi значит, что PHP работает отдельным процессом FastCGI, а не как mod_php: php_flag там неизвестен и останавливает Apache с ответом 500. sapi=apache2handler значит mod_php, где те же строки работают. Подробный разбор, значение каждого ответа и безопасная проверка того, что .htaccess вообще читается: Какой у вас сервер.

Какие правила где безопасны

Читайте строку так: слева группа директив, дальше что происходит на каждом сервере. Безопасно - правило работает как написано. Защитить - обернуть в IfModule или If, чтобы сервер без модуля пропустил блок, а не упал. Ломает сайт - одна неизвестная директива отвечает 500 для папки и всех папок ниже. Игнорируется - сервер вообще не читает .htaccess.

Группа директив Apache + mod_php Apache + FastCGI Apache за TLS-прокси Только nginx LiteSpeed
Redirect / RedirectMatch безопасно безопасно только абсолютный адрес https://: цель из одного пути добавляет шаг на http игнорируется безопасно
RewriteRule безопасно безопасно безопасно: схему задаёт переменная окружения игнорируется безопасно
php_flag / php_value безопасно ломает сайт безопасно: зависит от режима PHP игнорируется сверьтесь с документацией хостинга
Header защитить: <IfModule mod_headers.c> защитить: <IfModule mod_headers.c> защитить: <IfModule mod_headers.c> игнорируется: add_header в конфигурации сервера безопасно
Options безопасно безопасно безопасно игнорируется защитить: сверьтесь с документацией хостинга
AddHandler / SetHandler для PHP защитить защитить: не направляйте PHP в mod_php защитить игнорируется защитить

Директива, которую AllowOverride не разрешает в этой папке (Options, например), тоже отвечает 500.

Строки про FastCGI и PHP-FPM следуют руководству PHP о файлах .user.ini: php_value в .htaccess относится к режиму модуля Apache, а CGI и FastCGI читают .user.ini. Страница LiteSpeed о правилах rewrite говорит, что LiteSpeed читает правила rewrite в стиле Apache. Страница настроек PHP пользователя рассказывает про PHPRC и PHP_INI_SCAN_DIR и говорит, что .user.ini не рекомендуется в LSWS; она не говорит, что LiteSpeed берёт php_value из .htaccess, поэтому настройки PHP сверяйте с документацией хостинга. Защитные блоки и план отката: Безопасные правила, защитные блоки и откат.

Redirect: простые переносы

Одна страница переехала на новый адрес. mod_alias делает это одной строкой - сначала источник, потом цель:

Redirect 301 /old/page.php /new/page.php

301 ставьте для постоянного переноса (вес ссылок передаётся, браузер запоминает ответ), 302 - пока экспериментируете. Ошибочный 301 браузер помнит долго после исправления файла, поэтому проверяйте на 302 и переключайте на 301, когда уверены.

На хостинге, где HTTPS заканчивается на прокси спереди, цель из одного пути сначала отправляет посетителя на http://. Безопасные варианты: Redirect 301 или пара и Редиректы за HTTPS-прокси.

RedirectMatch: шаблоны

Обычный Redirect совпадает с префиксом адреса и приклеивает остаток к цели. Чтобы закрыть целое дерево одной строкой, задайте регулярное выражение с якорем:

RedirectMatch 301 ^/edu/suomen/.*$ /edu/index.php

Без регулярного выражения /edu/suomen/x.php попал бы на /edu/index.phpx.php. Точное совпадение одного адреса работает так же:

RedirectMatch 301 ^/robot/?$ /qa/robot/

Якорь /?$ значит, что подпути вроде /robot/basics/ не трогаются - перенаправляется только сам каталог.

Основы RewriteRule

Когда цель зависит от части источника, mod_rewrite захватывает её скобками и подставляет как $1, $2:

RewriteEngine On RewriteRule ^blog/([0-9]{4})/([a-z-]+)\.php$ /archive/$1/$2/ [R=301,L]

RewriteRule видит путь без ведущего слэша и без строки запроса. Чтобы сохранить параметры запроса, добавьте флаг QSA. Условие над правилом видит полный %{REQUEST_URI}, включая ведущий слэш, а захваченные группы возвращает как %1.

Важные флаги

Порядок правил

Apache проверяет правила сверху вниз, и жадное правило-префикс перекрывает всё ниже. Точные правила идут первыми, общие - последними:

Redirect 301 /pc/ /latex/ Redirect 301 /pc/LaTeX.php /latex/man/

Apache берёт первую совпавшую строку, и совпадение по префиксу перекрывает более точную строку ниже. /pc/LaTeX.php совпадает с префиксом /pc/, поэтому посетитель попадает на /latex/LaTeX.php, а не на /latex/man/: точную строку ставьте первой.

Оставьте комментарий о порядке в файле, иначе следующий редактор вернёт его обратно. Правила mod_rewrite выполняются раньше правил mod_alias независимо от порядка строк; не смешивайте обе системы в одном файле (почему).

Проверка правил

Не проверяйте сначала в браузере - кэш 301 обманывает. Спросите заголовки:

curl -s -o /dev/null -w "%{http_code} %{redirect_url}\n" https://example.com/robot/

301 https://example.com/qa/robot/

Проверяйте цепочку, а не только первый шаг: два редиректа подряд удваивают задержку и теряют вес в поиске. Цель - один шаг от старого адреса до конечной страницы.

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

Дисциплина та же, что в коде: одно правило на один перенос, комментарий над неочевидным правилом и периодическая проверка всех целей.

Старая глава про HTTPS-прокси теперь отдельная статья: Редиректы за HTTPS-прокси.

Статьи по теме

  1. Какой у вас сервер - заголовки ответа, PHP-зонд, модули Apache, процесс PHP, журнал ошибок и безопасная проверка того, что .htaccess читается.
  2. Безопасные правила, защитные блоки и откат - резервная копия, проверка в подпапке, блоки IfModule и If, и куда девать настройки PHP, когда php_flag нельзя.
  3. Redirect 301 или пара - когда хватает одной строки Redirect и когда нужна пара RewriteCond плюс RewriteRule.
  4. Редиректы за HTTPS-прокси - почему Apache за прокси пишет http:// и как этот сайт перевёл все редиректы на пары mod_rewrite.

Автор статьи: Андрей Олегович