Сайт перевели на свежий PHP, всё открывается, работает заметно быстрее. Через сутки выясняется, что форма с вложением молча ничего не отправляет. В логах PHP пусто, в логах Apache — обрыв соединения. Виноват оказался не PHP, а директива FcgidMaxRequestLen, у которой по умолчанию стоит потолок 128 КБ.
Заявка без файла уходит нормально. Заявка с приложенным документом — крутится и обрывается. Ошибка при этом не показывается: браузер получает пустой ответ, скрипт формы считает это сбоем сети и молчит.
Первое подозрение всегда падает на PHP, и там всё выглядит правильно:
upload_max_filesize = 32M
post_max_size = 32M
memory_limit = 256M
max_execution_time = 120
Значения щедрые, файл на 900 КБ в них помещается с большим запасом. Но до PHP этот запрос не доходит вовсе — его обрывает Apache, и в журнале ошибок остаётся строка вида mod_fcgid: HTTP request length 947 exceeds MaxRequestLen.
На старом сервере PHP работал модулем Apache. Модуль живёт внутри веб-сервера, и отдельного потолка на длину запроса у него нет — всё регулируется настройками самого PHP.
После переезда PHP запускается через mod_fcgid: веб-сервер и интерпретатор стали разными процессами, и между ними появился канал со своим ограничением. Ограничение это называется FcgidMaxRequestLen и по умолчанию равно 131 072 байтам, то есть 128 КБ. В конфигурации его никто не писал — оно просто есть.
Отсюда главный вывод, ради которого стоит читать дальше: при переезде смотреть надо не на номер версии PHP, а на схему запуска. Версия меняет язык, схема меняет правила игры.
Одна строка, положенная во временный файл в корне сайта:
<?php echo php_sapi_name();
Что означает ответ:
| Ответ | Схема | На что влияет |
|---|---|---|
apache2handler | модуль внутри Apache | потолка на длину запроса нет, но процессы тяжёлые |
fpm-fcgi | PHP-FPM | быстрее всего, ограничения задаются в пуле и в nginx |
cgi-fcgi | mod_fcgid или mod_proxy_fcgi | здесь и живёт FcgidMaxRequestLen |
cli | командная строка | к сайту отношения не имеет, свой отдельный php.ini |
Временный файл после проверки надо убрать: он рассказывает о сервере больше, чем стоит рассказывать посторонним.
Это вторая ловушка, и она обиднее первой. Файлов php.ini на сервере несколько: свой у командной строки, свой у веб-схемы, плюс .user.ini в каталоге сайта и настройки панели хостинга поверх всего этого. Правка «того самого» php.ini через SSH меняет поведение php -v и не меняет ничего на сайте.
Действующие значения показывает только запуск тем же способом, каким работает сайт:
<?php
foreach ([
'upload_max_filesize', 'post_max_size', 'memory_limit',
'max_execution_time', 'max_input_vars', 'max_file_uploads',
'default_socket_timeout', 'output_buffering',
] as $k) {
printf("%-24s %s\n", $k, ini_get($k));
}
На одном из переездов такая сверка показала, что три директивы из восьми разъехались молча: в файле стояло одно, действовало другое. Ни одна из них не роняла сайт заметно — они меняли поведение под нагрузкой, и это худший вид поломки, потому что находится он позже всех.
Директива ставится в конфигурации Apache, не в php.ini, и требует перезапуска, а не перечитывания конфигурации:
<IfModule mod_fcgid.c>
FcgidMaxRequestLen 33554432
</IfModule>
33 554 432 байта — это 32 МБ, ровно столько же, сколько разрешает upload_max_filesize. Держать эти два числа согласованными важнее, чем сделать их большими: если канал уже, чем разрешает PHP, пользователь получает молчаливый обрыв вместо понятного «файл слишком большой».
После переезда сайт действительно открывается заметно быстрее, и это легко записать в заслугу новой версии языка. Обычно дело не в ней.
Кратный рост дают две вещи: переход от порождения процесса на каждый запрос к постоянно живущим процессам и включённый кэш опкода. Номер версии добавляет свою долю, но она меньше, чем принято думать.
Практический смысл этой поправки простой: «стало в пять раз быстрее, чем было» — это не то же самое, что «выжато всё». Пока не сверены действующие значения директив и не проверен кэш опкода, работа не закончена, даже если заказчик уже доволен цифрой.
php_sapi_name().ini_get(), а не глазами по файлу.fcgid выставить FcgidMaxRequestLen согласованно с post_max_size.opcache.enable и opcache.memory_consumption.Он не ловит ошибки самого кода на новой версии языка: убранные функции, изменившееся поведение при сравнении типов, предупреждения, ставшие ошибками. Для этого нужен отдельный прогон и чтение логов под настоящим трафиком, а не после переезда в тишине.
И он ничего не говорит о базе данных. Смена версии PHP часто идёт вместе со сменой версии MySQL или MariaDB, и там свои сюрпризы — строгий режим и режимы сортировки на первом месте.
Похожая история про то, как настройка по умолчанию оказывается настоящей причиной сбоя, — разбор ошибки 525 у Cloudflare, где виноваты 768 соединений, поставленные без нашего участия. И про сжатие, которое работало только для HTML, потому что список типов в конфиге был закомментирован.
Если сайт работает через mod_fcgid, размер запроса ограничивает директива FcgidMaxRequestLen, а не upload_max_filesize. По умолчанию она равна 131 072 байтам — 128 КБ. Всё, что тяжелее, обрывается на уровне Apache, до того как запрос дойдёт до PHP, поэтому в логах PHP не остаётся ничего.
Функция php_sapi_name() возвращает имя схемы: apache2handler — модуль Apache, fpm-fcgi — PHP-FPM, cgi-fcgi — mod_fcgid или mod_proxy_fcgi, cli — командная строка. Номер версии PHP об этом не говорит ничего, а от схемы зависят и скорость, и ограничения на размер запроса.
Файлов php.ini на сервере обычно несколько — свой у CLI, свой у веб-схемы, плюс .user.ini и настройки панели хостинга. Действующие значения показывает phpinfo() или ini_get(), запущенные тем же способом, каким работает сайт. На одном из переездов так разошлись три директивы из восьми.
Обычно это сравнение с тем, что было, а не с тем, что возможно. Кратный рост чаще даёт переход с CGI на постоянный процесс и включённый кэш опкода, а не сам номер версии. Пока не сверены действующие значения директив, считать работу законченной рано.
Переезжаете на новый PHP или уже переехали и что-то ведёт себя странно? Сниму действующие значения, сверю со старым сервером и покажу, что разъехалось. Диагностика бесплатная, отчёт остаётся у вас.
Настройка сервера от 2 000 ₽