Настройка Apache PHP-FPM приводит к ошибке “Файл не найден”

Вопрос или проблема

Я много искал в Google, и действительно есть много результатов (даже на serverfault.com) по этой проблеме. Однако мне не удалось решить свои проблемы с ней.

Моя настройка

Я настраиваю Ubuntu 20 на WSL2 и пытаюсь обеспечить его с помощью Ansible. Я пытаюсь настроить Apache с PHP-FPM-7.4.

Apache

Apache, похоже, работает нормально. У меня есть корневая директория с файлом test.html. Когда я вызываю прямой URL к этому HTML-файлу, я вижу его содержимое, как и положено.

Vhost

Примечание: для удобства чтения я пропустил все комментарии

DirectoryIndex index.php index.html

<VirtualHost *:80>
  ServerName paul.test
  ServerAlias *.paul.test

  <FilesMatch \.php$>
    Require all granted
    SetHandler proxy:fcgi://127.0.0.1:9000
  </FilesMatch>

  UseCanonicalName Off
  VirtualDocumentRoot /var/www/hosts/%-4.0.%-3.0/%-5+

  <Directory "/var/www">
    AllowOverride All
    Options -Indexes +FollowSymLinks
    Require all granted
  </Directory>
  ProxyPassMatch ^/(.*\.php(/.*)?)$ "fcgi://127.0.0.1:9000/var/www/hosts/%-4.0.%-3.0/%-5+"
</VirtualHost>

<VirtualHost *:443>
  ServerName paul.test
  ServerAlias *.paul.test

  <FilesMatch \.php$>
    Require all granted
    SetHandler proxy:fcgi://127.0.0.1:9000
  </FilesMatch>

  UseCanonicalName Off
  VirtualDocumentRoot /var/www/hosts/%-4.0.%-3.0/%-5+

  SSLEngine on
  SSLCipherSuite AES256+EECDH:AES256+EDH
  SSLProtocol All -SSLv2 -SSLv3
  SSLHonorCipherOrder On
  SSLCompression off
  SSLCertificateFile /etc/ssl/certs/ssl-cert-snakeoil.pem
  SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key

  <Directory "/var/www">
    AllowOverride All
    Options -Indexes +FollowSymLinks
    Require all granted
  </Directory>
  ProxyPassMatch ^/(.*\.php(/.*)?)$ "fcgi://127.0.0.1:9000/var/www/hosts/%-4.0.%-3.0/%-5+"
</VirtualHost>

PHP FPM

Теперь проблема в том, что когда я вызываю PHP-файл: тогда я вижу “Файл не найден.” в веб-браузере.

pool.d/www.conf

Примечание: для удобства чтения я пропустил все комментарии

[www]

user = www-data
group = www-data

listen = 127.0.0.1:9000

listen.owner = www-data
listen.group = www-data

pm = dynamic
pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 5

listen.allowed_clients = 127.0.0.1

Журналы ошибок

Журнал ошибок PHP-FPM говорит, что он работает нормально:

[24-Dec-2020 12:16:06] NOTICE: fpm is running, pid 26532  
[24-Dec-2020 12:16:06] NOTICE: ready to handle connections  
[24-Dec-2020 12:16:06] NOTICE: systemd monitor interval set to 10000ms

Журнал ошибок Apache показывает ошибку:

[Thu Dec 24 12:44:38.779511 2020] [proxy_fcgi:error] [pid 26424:tid 140510559713024] [client ::1:57178] AH01071: Got error 'Primary script unknown'

Как исправить / исследовать?

Для начала, я нахожу сообщение об ошибке “Файл не найден.” очень неясным. Кто не нашел какой файл? Это помогло бы мне и другим, кто столкнется с этой ошибкой в будущем, объяснить, кто вызывает эту ошибку и что это на самом деле означает.

Мой вывод на данный момент таков, что сообщение об ошибке выдается Apache и означает, что он не может найти процесс PHP-FPM. Это верно?

Как мне дальше исследовать и исправить это?

Правка: Добавляю свою настройку Apache и PHP-FPM.

Правка / важное примечание

В процессе улучшения этого поста и исследования проблемы я выяснил, что базовая настройка Apache и PHP-FPM работает нормально.

Проблема, по-видимому, вызвана следующим оператором:

ProxyPassMatch ^/(.*\.php(/.*)?)$ "fcgi://127.0.0.1:9000/var/www/hosts/%-4.0.%-3.0/%-5+"

Этот оператор работал нормально на предыдущей настройке (с Centos), но сейчас не работает. Когда я заменяю этот оператор на статический путь к корню документа, он работает нормально.

Я буду исследовать дальше, но если кто-то знает, как это исправить, пожалуйста, дайте знать.

После дальнейшего расследования я выяснил, что моя ProxyPassMatch инструкция вызвала ошибку. А затем я нашел пост на StackOverflow, в котором говорилось, что весь оператор ProxyPassMatch не следует использовать.

Просто SetHandler proxy:fcgi://127.0.0.1:9000 (который уже был в моем vhost) заставляет PHP работать.

Поскольку это первый результат, который Google дал мне, когда я искал “apache php-fpm file not found”, я хотел бы добавить свой ответ здесь.

Если корень документа, доступный для Apache, отличается от корня документа, доступного для PHP-FPM

В моем случае корень документа, доступный для Apache, был /data/.../wwwroot, а корень документа для PHP-FPM (который работает в контейнере) был /var/www/html.

Исправление заключалось в том, чтобы настроить DOCUMENT_ROOT и SCRIPT_FILENAME с помощью ProxyFCGISetEnvIf:

Define container_root "/var/www/html"
<FilesMatch "\.php$">
    <If "-f %{REQUEST_FILENAME}">
        SetHandler "proxy:fcgi://127.0.0.1:9000"
        ProxyFCGISetEnvIf "true" DOCUMENT_ROOT   "${container_root}"
        ProxyFCGISetEnvIf "true" SCRIPT_FILENAME "%{HANDLER}${container_root}%{reqenv:SCRIPT_NAME}"
    </If>
</FilesMatch>
UnDefine container_root

(Примечание о различных синтаксисах доступа к переменным: каждый синтаксис предназначен для переменных из разных источников. ${} используется для того, что определено с помощью Define или с помощью аргумента командной строки -D; %{} используется для переменных, определенных Apache; а %{reqenv:} – это вызов функции для доступа к внутренней переменной окружения.)

Вы можете отладить эти переменные, добавив следующие строки в конце раздела FilesMatch:

Header always set X-HANDLER         "expr=%{HANDLER}"
Header always set X-DOCUMENT_ROOT   "expr=%{reqenv:DOCUMENT_ROOT}"
Header always set X-SCRIPT_FILENAME "expr=%{reqenv:SCRIPT_FILENAME}"
Header always set X-SCRIPT_NAME     "expr=%{reqenv:SCRIPT_NAME}"

После этого вы должны увидеть их в заголовках ответа.

Просто добавлю немного информации на случай, если кто-то захочет исправить проблему, но для Symfony ответ от @MichaelK верный.

Просто Define container_root "/var/www/html/public" должен указывать на публичную папку symfony внутри контейнера.

Надеюсь, это поможет кому-то.

Ответ или решение

Настройка Apache с PHP-FPM: ошибка "Файл не найден"

При настройке сервера Apache с поддержкой PHP-FPM на WSL2 (Windows Subsystem for Linux) с использованием Ansible, многие пользователи сталкиваются с ошибкой "Файл не найден" при попытке доступа к PHP-файлам. Данная проблема может быть несколько запутанной, поскольку сообщение об ошибке не всегда однозначно указывает на источник проблемы.

Основные аспекты конфигурации

1. Виртуальный хост Apache

Ваша конфигурация виртуального хоста выглядит следующим образом:

<DirectoryIndex index.php index.html>

<VirtualHost *:80>
    ServerName paul.test
    ServerAlias *.paul.test

    <FilesMatch \.php$>
        Require all granted
        SetHandler proxy:fcgi://127.0.0.1:9000
    </FilesMatch>

    UseCanonicalName Off
    VirtualDocumentRoot /var/www/hosts/%-4.0.%-3.0/%-5+

    <Directory "/var/www">
        AllowOverride All
        Options -Indexes +FollowSymLinks
        Require all granted
    </Directory>

    ProxyPassMatch ^/(.*\.php(/.*)?)$ "fcgi://127.0.0.1:9000/var/www/hosts/%-4.0.%-3.0/%-5+"
</VirtualHost>
2. Конфигурация PHP-FPM

Конфигурация pool.d/www.conf фрагментирована следующим образом:

[www]
user = www-data
group = www-data
listen = 127.0.0.1:9000
listen.owner = www-data
listen.group = www-data
pm = dynamic
pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 5
listen.allowed_clients = 127.0.0.1

Анализ проблемы

Ошибка, записанная в логах Apache:

[proxy_fcgi:error] [pid 26424:tid 140510559713024] [client ::1:57178] AH01071: Got error 'Primary script unknown'

указывает на то, что Apache не может найти скрипт. Это связано с тем, что конфигурация ProxyPassMatch может не соответствовать фактическому пути PHP-скрипта, к которому пытается обратиться Apache.

Решение проблемы

1. Упрощение настройки

Как было выяснено, использование директивы ProxyPassMatch может не быть необходимым в вашем случае. Достаточно установить SetHandler для обработки PHP-скриптов:

<FilesMatch "\.php$">
    Require all granted
    SetHandler proxy:fcgi://127.0.0.1:9000
</FilesMatch>
2. Убедитесь, что пути соответствуют

Если ваш Apache-корень и корень PHP-FPM находятся в разных местах, необходимо явно задать их с помощью переменных окружения. Например, используйте ProxyFCGISetEnvIf для определения корневых директорий:

Define container_root "/var/www/html"
<FilesMatch "\.php$">
    <If "-f %{REQUEST_FILENAME}">
        SetHandler "proxy:fcgi://127.0.0.1:9000"
        ProxyFCGISetEnvIf "true" DOCUMENT_ROOT   "${container_root}"
        ProxyFCGISetEnvIf "true" SCRIPT_FILENAME "%{HANDLER}${container_root}%{reqenv:SCRIPT_NAME}"
    </If>
</FilesMatch>
UnDefine container_root
3. Отладка переменных

Добавьте следующие заголовки, чтобы видеть значения переменных при отладке:

Header always set X-HANDLER         "expr=%{HANDLER}"
Header always set X-DOCUMENT_ROOT   "expr=%{reqenv:DOCUMENT_ROOT}"
Header always set X-SCRIPT_FILENAME "expr=%{reqenv:SCRIPT_FILENAME}"
Header always set X-SCRIPT_NAME     "expr=%{reqenv:SCRIPT_NAME}"

Это поможет вам удостовериться в правильности настроек и путей.

Заключение

Как показывает практика, проблема "Файл не найден" при настройке Apache с PHP-FPM часто возникает из-за неверной конфигурации путей к файлам. Упрощая обработку PHP и обеспечивая соответствие между корнями, вы сможете устранить эту проблему. Ключом к успешной настройке является внимание к деталям в конфигурации и открытие для отладки значений переменных. Если возникают дополнительные трудности, рассмотрите возможность использования статического пути для начала, чтобы подтвердить работоспособность основного сценария.

Оцените материал
Добавить комментарий

Капча загружается...