Какой у вас сервер: проверка до правки .htaccess
Заголовки ответа
Начните с одного запроса и прочитайте заголовок server. Инструменты здесь: curl, SSH, grep.
curl -sSI https://example.com/
HTTP/2 200 content-type: text/html; charset=UTF-8 server: Apache
Заголовок - подсказка, а не диагноз. Прокси спереди скрывает настоящий сервер, а хостинг может заменить или убрать заголовок. Он не говорит, работает PHP как mod_php или отдельным процессом, а именно от этого зависит, превратится ли рабочий .htaccess в 500.
PHP-файл-зонд
На остальное отвечает крошечный PHP-файл. Дайте ему имя, которое никто не угадает, и удалите после проверки:
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
rm ~/example.com/probe-7f3a.php
В этом ответе TLS заканчивается в Apache (HTTPS=on), заголовка прокси нет (XFP=NULL), а PHP не модуль Apache (sapi=cgi-fcgi). Следующий раздел объясняет три значения SAPI.
Что значит PHP_SAPI
| Значение | Как работает PHP | Что это значит для .htaccess |
|---|---|---|
| apache2handler | mod_php внутри Apache | php_flag и php_value работают. |
| cgi-fcgi | процесс CGI или FastCGI, часто mod_fcgid или обёртка вроде php85.cgi | php_flag неизвестен: защитите его или перенесите настройку в .user.ini или файл хостинга. |
| fpm-fcgi | PHP-FPM | php_flag неизвестен: используйте .user.ini или настройки пула. |
| cli | командная строка, а не веб-запрос | .htaccess тут вообще нет: зонд из оболочки ничего не доказывает про веб-сервер. |
Эти же значения решают, куда класть настройки PHP. mod_php читает php_flag из .htaccess; CGI, FastCGI и PHP-FPM читают .user.ini или ini файл хостинга. Точные файлы и команды перезапуска: Безопасные правила.
Модули Apache
На локальном Docker php:8.5-apache с mod_php спросите сам Apache, какие модули загружены:
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)
php_module (shared) - это mod_php. На хостинге с FastCGI строки php_module нет, хотя Apache остаётся Apache. alias_module нужен для Redirect и RedirectMatch, rewrite_module для RewriteRule, headers_module для Header, а version_module для IfVersion. Отсутствующий модуль - ровно то, для чего нужны защитные блоки из Безопасных правил. Общий хостинг обычно не даёт apache2ctl, поэтому там остаются файл-зонд и панель хостинга.
Какой процесс PHP работает
Когда Apache только фронтенд, у процесса PHP своё имя. На FastCGI-хостинге (DreamHost) это выглядит так:
pgrep -l -u user php
2745746 php85.cgi
ls /usr/sbin/apache2*
/usr/sbin/apache2 /usr/sbin/apache2ctl
php85.cgi - рабочий процесс FastCGI, значит PHP не внутри Apache. Хостинг с PHP-FPM показывает процесс php-fpm. Наличие apache2ctl подтверждает Apache, но не подтверждает mod_php.
Где журнал ошибок
Сломанное правило не молчит. Apache пишет имя файла и директиву в журнал ошибок:
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
На общем хостинге журнал обычно лежит в домашней папке, по папке на протокол; в Debian и Ubuntu это часто /var/log/apache2/error.log; панель управления показывает путь в просмотре журналов. Если ничего из этого нет, спросите хостинг. Строка config.c значит, что Apache остановился при чтении конфигурации, а путь после неё называет точный файл. Настоящий разбор такого 500: Разбор ошибок PHP.
Читается ли .htaccess вообще
nginx вообще не читает .htaccess, а Apache можно заставить его игнорировать через AllowOverride None. Проверяйте во временной подпапке, никогда на главной странице. Намеренно неверная строка ответит 500 только там:
mkdir -p ~/example.com/test-htaccess
vi ~/example.com/test-htaccess/.htaccess
ThisDirectiveDoesNotExist on
На локальном Apache 2.4.68 с mod_php тестовая папка /test-htaccess/ на http://localhost:8080 отвечает 500, а её родитель по-прежнему 200:
curl -sS -o /dev/null -w "%{http_code}\n" http://localhost:8080/test-htaccess/
500
curl -sS -o /dev/null -w "%{http_code}\n" http://localhost:8080/
200
500 в тестовой папке и 200 у её родителя значат, что сервер читает .htaccess здесь: причина только в неверной строке. 500 на обеих страницах указывает на конфигурацию сервера; 200 на обеих - .htaccess в этой папке игнорируется, и никакой редирект там не заработает. В любом случае удалите тестовую папку:
rm -rf ~/example.com/test-htaccess
Второй безопасный зонд - редирект: добавьте одно правило в тестовую папку и спросите его источник через curl -sI. Ответ 301 доказывает, что файл читается; 404 - что нет. Этот тест меняет ответ только одной папки.
Где заканчивается TLS
Зонд отвечает на это без догадок:
- HTTPS='on': TLS заканчивает сам Apache, как на DreamHost или на VPS с сертификатом certbot. Цель Redirect из одного пути сохраняет https.
- HTTPS пустой, а XFP='https': TLS заканчивает прокси спереди, как на Beget, и Apache видит обычный HTTP с заголовком X-Forwarded-Proto: https. Цель из одного пути станет http://; используйте абсолютный адрес или пару rewrite из Redirect 301 или пара.
- Оба пустые на https-адресе: TLS может заканчиваться на прокси без заголовка, или сам зонд настроен неверно. Не стройте правило https только на этом ответе; спросите хостинг.
Полная история Apache за прокси, лишний шаг на http и два условия, которые останавливают цикл: Редиректы за HTTPS-прокси.
Автор статьи: Arthur Isaev