вторник, 16 июля 2024 г.

NGINX и PHP-FPM. "Грабли" с doc_root

Вводные. 

Unix-like система. Стоит Nginx v1.25.3 и PHP-FPM v8.3.2
Теперь важно. В php.ini параметр doc_root = "/opt/share/www". 
Допустим наш сайт лежит в /opt/share/www/site1. Там есть index.php следующего содержания:
<?php echo __FILE__;
Ну, то есть, примитивизм - в случае обращение к этому файлу, он будет выдавать свой абсолютный путь.

Конфигурация nginx выглядит примерно так:

	location /api/ {
		root /opt/share/www/site1;
		default_type "application/json";
		autoindex off;

		fastcgi_pass unix:/opt/var/run/php8-fpm.sock;
		include fastcgi_params;
		
		fastcgi_split_path_info ^(/api)(/.+)$;
		fastcgi_param SCRIPT_FILENAME $document_root/index.php;
		fastcgi_param PATH_INFO $fastcgi_path_info;
		fastcgi_param REQUEST_URI $fastcgi_path_info;
	}

Так вот в такой ситуации при обращении по адресу http://site1/api/blablabla (подразумевается что site1 маппится на нужный для nginx IP и порт) вам в браузер будет выдаваться "No input file specified". При этом в error.log будет что-то типа "Unable to open primary script: /opt/share/www/site1/index.php (No such file or directory)".

Если заглянуть в nginx/fastcgi_params, то мы там кстати увидим, что никакой SCRIPT_FILENAME не задаётся, а задаётся SCRIPT_NAME. Может это его надо переопределить? 

Допишем его в конфиг nginx аналогично SCRIPT_FILENAME:

  
  include fastcgi_params;
  ...
  fastcgi_param SCRIPT_FILENAME $document_root/index.php;
  fastcgi_param SCRIPT_NAME $document_root/index.php;
  ...

Ну и, понятное дело, перезагрузим конфигурацию nginx через "nginx -s reload"

Это ничего не даст. Ошибка будет та же.

Если же убрать из конфига nginx передачу параметра SCRIPT_FILENAME, то ошибка будет другая: в браузере будет "File not found", а в error.log - "Primary script unknown".

Ну ОК. Вы скажете: "Чувак, у тебя задан doc_root, вот и надо указывать путь к index.php не абсолютный путь относительно корня, а от /opt/share/www !"

Ха, если бы все было так просто. Ну смотрите. Выкидываем $document_root из параметров и делаем так:

  
  include fastcgi_params;
  ...
  fastcgi_param SCRIPT_FILENAME /site1/index.php;
  fastcgi_param SCRIPT_NAME /site1/index.php;
  ...
Следуя логике, у нас /opt/share/www соединится с /site1/index.php, и мы получим абсолютный путь. 
Перечитываем конфигурацию nginx. 
Переходим по http://site1/api/blablabla. 
Получаем "Primary script unknown"... 

И как бы мы не извращались передавая только SCRIPT_FILENAME или только SCRIPT_NAME - ошибка та же. 

А теперь, собственно, в чём прикол? Ну и решение. 
SCRIPT_FILENAME - это параметр для php-fpm. И он НЕ зависит от doc_root в php.ini. 
SCRIPT_NAME - это параметр для php. И он зависит от doc_root в php.ini. 

Таким образом, правильной конфигурацией nginx будет:
  
  include fastcgi_params;
  ...
  fastcgi_param SCRIPT_FILENAME $document_root/index.php;
  fastcgi_param SCRIPT_NAME /site1/index.php;
  ...
В таком случае, в нашем примере, в конечном итоге оба параметра будут ссылаться на /opt/share/www/site1/index.php 

ЗЫ. Причем, что интересно, SCRIPT_FILENAME может указывать на другой php-файл (может быть даже не на php-файл - не проверял) - главное чтобы этот файл существовал. А выполнится при этом тот файл, который указан именно в SCRIPT_NAME. 

ЗЗЫ. Естественно, если просто закоментировать doc_root в php.ini, то первоначальная конфигурация будет работать.