Apache 2.4 + PHP-FPM + ProxyPassMatch

Я недавно установил Apache 2.4 на свою локальную машину вместе с PHP 5.4.8, используя PHP-FPM.

Все прошло довольно гладко (через некоторое время...), но все еще есть странная ошибка:

Я настроил Apache для PHP-FPM следующим образом:

<VirtualHost *:80>
    ServerName localhost
    DocumentRoot "/Users/apfelbox/WebServer"
    ProxyPassMatch ^/(.*\.php(/.*)?)$ fcgi://127.0.0.1:9000/Users/apfelbox/WebServer/$1
</VirtualHost>

Это работает, например, если я позвоню http://localhost/info.php Я правильно понимаю phpinfo() (это просто тестовый файл).

Однако, если я позвоню в каталог, я получу 404 с телом File not found. и в журнале ошибок:

[Tue Nov 20 21:27:25.191625 2012] [proxy_fcgi:error] [pid 28997] [client ::1:57204] AH01071: Got error 'Primary script unknown\n'

Обновить

Теперь я попытался сделать проксирование с помощью mod_rewrite:

<VirtualHost *:80>
    ServerName localhost
    DocumentRoot "/Users/apfelbox/WebServer"

    RewriteEngine on    
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule ^/(.*\.php(/.*)?)$ fcgi://127.0.0.1:9000/Users/apfelbox/WebServer/$1 [L,P]
</VirtualHost>

Но проблема в том, что он всегда перенаправляет, потому что на http://localhost/ автоматически http://localhost/index.php запрашивается, из-за

DirectoryIndex index.php index.html

Обновление 2

Хорошо, я думаю, что "возможно, сначала проверьте, есть ли файл, который нужно передать прокси-серверу:

<VirtualHost *:80>
    ServerName localhost
    DocumentRoot "/Users/apfelbox/WebServer"

    RewriteEngine on    
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} -f
    RewriteRule ^/(.*\.php(/.*)?)$ fcgi://127.0.0.1:9000/Users/apfelbox/WebServer/$1 [L,P]
</VirtualHost>

Теперь полное переписывание больше не работает...

Обновление 3

Теперь у меня есть это решение:

<VirtualHost *:80>
    ServerName localhost
    DocumentRoot "/Users/apfelbox/WebServer"

    RewriteEngine on    
    RewriteCond /Users/apfelbox/WebServer/%{REQUEST_FILENAME} -f
    RewriteRule ^/(.*\.php(/.*)?)$ fcgi://127.0.0.1:9000/Users/apfelbox/WebServer/$1 [L,P]
</VirtualHost>

Сначала проверьте, есть ли файл для передачи в PHP-FPM (с полным и абсолютным путем), а затем выполните перезапись.

Это не работает при использовании перезаписи URL внутри подкаталога, а также не работает для таких URL, как http://localhost/index.php/test/Итак, вернемся к исходной точке.


Есть идеи?

12 ответов

После нескольких часов поиска и прочтения документации по Apache я пришел к решению, которое позволяет использовать пул, а также позволяет директиве Rewrite в.htaccess работать, даже если URL содержит файлы.php.

<VirtualHost ...>

 ...

 # This is to forward all PHP to php-fpm.
 <FilesMatch \.php$>
   SetHandler "proxy:unix:/path/to/socket.sock|fcgi://unique-domain-name-string/"
 </FilesMatch>

 # Set some proxy properties (the string "unique-domain-name-string" should match
 # the one set in the FilesMatch directive.
 <Proxy fcgi://unique-domain-name-string>
   ProxySet connectiontimeout=5 timeout=240
 </Proxy>

 # If the php file doesn't exist, disable the proxy handler.
 # This will allow .htaccess rewrite rules to work and 
 # the client will see the default 404 page of Apache
 RewriteCond %{REQUEST_FILENAME} \.php$
 RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_URI} !-f
 RewriteRule (.*) - [H=text/html]

</VirtualHost>

Согласно документации Apache, для параметра прокси SetHandler требуется HTTP-сервер Apache 2.4.10.

Я надеюсь, что это решение поможет вам тоже.

Я столкнулся и с этой проблемой вчера - Apache 2.4 перешел из Debian/ экспериментальный в Debian/ нестабильный, заставив меня заняться этим новым делом; не на наших производственных серверах конечно;).

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

Вот что работает для сайта CakePHP и PHPMyAdmin (последний нуждается в некоторой конфигурации, если вы используете пакеты Debian), поэтому я могу подтвердить, что mod_rewrite по-прежнему работает, как и ожидалось, чтобы сделать красивое переписывание URL.

уведомление DirectoryIndex index.phpЭто может быть причиной того, что ни один из ваших конфигов не работал для "папок" (по крайней мере, это то, что здесь не работает).

Я все еще получаю File not found. для каталогов, но только если нет индексного файла, он может разобрать. Хотелось бы избавиться и от этого, но это не так критично, как сейчас.


<VirtualHost *:80>
    ServerName site.localhost

    DocumentRoot /your/site/webroot
    <Directory />
            Options FollowSymlinks
            DirectoryIndex index.php
            AllowOverride All
            Require all granted
    </Directory>

    <LocationMatch "^(.*\.php)$">
            ProxyPass fcgi://127.0.0.1:9000/your/site/webroot
    </LocationMatch>

    LogLevel debug
    ErrorLog /your/site/logs/error.log
    CustomLog /your/site/logs/access.log combined
</VirtualHost>

Вышеупомянутый vhost прекрасно работает с.htaccess в корне следующим образом:

<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^(.*)$ index.php [QSA,L]
</IfModule>

Я не совсем понимаю, что вы подразумеваете под URL rewriting inside a subdirectory хотя (я только переписываю в index.php рута).


(О, и вам нужно убедиться, что Xdebug не конфликтует с FPM в вашей системе, из коробки они хотят использовать те же порты.)

Все, что вам нужно сделать, это установить:

 ProxyErrorOverride on

И не забудьте установить страницу клиента с помощью:

ErrorDocument 404 /path/to/error_page_file    

Это то, что у меня есть. Кажется, работает нормально. Я поместил Drupal в подкаталог, и он переписывает работу, индексы каталогов работают, и PATH_INFO работает.

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} ^/((.*\.php)(/.*)?)$
RewriteCond %2 -f
RewriteRule . fcgi://127.0.0.1:9000/%1 [L,P]
RewriteOptions Inherit

Я пытался сделать что-то подобное без переписывания ("Если" и тому подобное), но я не мог заставить что-либо работать.

РЕДАКТИРОВАТЬ: Обратите внимание, что если вы собираетесь реализовать это в качестве поставщика общего хостинга, это может быть проблемой безопасности. Это позволит пользователям передавать PHP-скрипты произвольному fcgi-прокси. Если бы у вас был отдельный пул для каждого пользователя, это позволило бы атаковать с повышением привилегий.

Еще одно решение (требуется Apache >= 2.4.10) - внутри vhost:

# define worker
<Proxy "unix:/var/run/php5-fpm-wp.bbox.nuxwin.com.sock|fcgi://domain.tld" retry=0>
    ProxySet connectiontimeout=5 timeout=7200
</Proxy>

<If "%{REQUEST_FILENAME} =~ /\.php$/ && -f %{REQUEST_FILENAME}">
    SetEnvIfNoCase ^Authorization$ "(.+)" HTTP_AUTHORIZATION=$1
    SetHandler proxy:fcgi://domain.tld
</If>

Поэтому здесь, обработчик fcgi для PHP будет установлен, только если файл существует и если его имя совпадает с расширением файла PHP.

Кстати: для тех, кто хотел бы установить ProxyErrorOverride на On, имейте в виду, что это действительно плохая идея. Использование этой директивы не без каких-либо проблем. Например, любое приложение PHP, отправляющее код HTTP, такой как 503, может привести к неожиданному результату. Обработчик ошибок по умолчанию будет задействован в любых случаях и для приложений PHP, которые предоставляют API, это действительно плохое поведение.

Одна вещь, с которой я столкнулся при решении этой проблемы, заключается в том, что если вы используете комбинацию:

chroot = /path/to/site
chdir = /

В вашей конфигурации пула fpm не передавайте полный путь к ProxyPass директивы.

ProxyPass fcgi://127.0.0.1:9020/$1

Но, ТОЛЬКО, если пул на этом порте хромирован.

Я не уверен, что проблема связана, но я нашел частично работающее решение здесь:

https://stackoverflow.com/questions/44054617/mod-rewrite-in-2-4-25-triggering-fcgi-primary-script-unknown-error-in-php-fpm

Уловка, кажется, добавляет? char в.htaccess RewriteRule, например, используя:

RewriteRule ^(.*)$ index.php?/$1 [L,NS]

вместо:

RewriteRule ^(.*)$ index.php/$1 [L,NS]

Похоже, источником проблемы является изменение в mod_rewrite в Apache 2.4.25. Я использовал уровень журнала Apache trace1 для наблюдения за "циклом", который передает $1 в php-fpm после того, как index.php/$1 был пройден. $1 генерирует ошибку "AH01071: Ошибка получена:" Основной сценарий неизвестен \ n "".

Надеюсь, этот маленький кусочек поможет кому-то решить свои проблемы.

Лучший способ решить эту проблему - включить журналы отладки для mod_proxy, mod_rewrite и php-fpm. В apache 2.4 теперь вы можете включить журналы отладки только для определенных модулей. http://httpd.apache.org/docs/current/mod/core.html Конфигурация для каждого модуля и для каждого каталога доступна в Apache HTTP Server 2.3.6 и более поздних версиях.

Может быть, вы получаете двойной удар по каталогам?

Вот что я использую, и это прекрасно работает:

<LocationMatch ^(.*\.php)$>
  ProxyPass fcgi://127.0.0.1:9000/home/DOMAINUSER/public_html$1
</LocationMatch>

Я сталкиваюсь с такими же проблемами на моем сервере (Centos 7.3.16 Docker). После отслеживания журнала php-fpm я обнаружил пропущенную системную библиотеку. WARNING: [pool www] child 15081 said into stderr: "php-fpm: pool www: symbol lookup error: /lib64/libnsssysinit.so: undefined symbol: PR_GetEnvSecure" затем я перезагружаю nspr, он работает. Если вы не можете найти решения после попытки каких-либо методов, вы можете попробовать это. yum -y install/reinstall nspr

У меня ошибка также после перехода на php-fpm + apache 2.4.6 для экземпляров drupal

но я использую мод события mpm

просто вставьте

DirectoryIndex index.php работает для меня

тогда мои настройки Vhost выглядят как ниже

<VirtualHost *:8080>
  ServerAdmin [email protected]
  ServerName sever.com
  DocumentRoot /var/www/html/webroot
    ErrorLog logs/web-error_log
    CustomLog logs/web-access_log common
<IfModule mpm_event_module>
    ProxyPassMatch ^/(.*\.php(/.*)?)$ fcgi://127.0.0.1:9000/var/www/html/webroot/$1
</IfModule>
  <Directory /var/www/html/webroot>
     Options FollowSymlinks
     DirectoryIndex index.php
     AllowOverride All
     Require all granted
  </Directory>
</VirtualHost>

Спасибо

нет необходимости пересматривать файл.htaccess по умолчанию для drupal

Линоде имеет большой учебник на эту тему

В основном вы устанавливаете обработчик для всего сервера, который будет перехватывать любые php-скрипты и передавать их в fast-cgi.

Это работает с Wordpress 5.1.1 и новее вместе с PHP 7.3, FastCGI, прокси, а также MariaDB/MySQL. Проверено дважды на моих серверах. Работает как шарм.

Сначала на CentOS/Fedora/Red Hat

sudo yum remove php*
sudo yum --enablerepo=extras install epel-release
sudo yum install php-fpm php-mysql php-gd php-imap php-mbstring 
sudo grep -E '(proxy.so|fcgi)' /etc/httpd/conf.modules.d/00-proxy.conf
sudo mv /etc/httpd/conf.d/php.conf /etc/httpd/conf.d/php.conf_bak

Отредактируйте этот файл:

sudo nano /etc/php-fpm.d/www.conf

Вставьте это:

[www]

; The address on which to accept FastCGI requests.
; Valid syntaxes are:
;   'ip.add.re.ss:port'    - to listen on a TCP socket to a specific address on
;                            a specific port;
;   'port'                 - to listen on a TCP socket to all addresses on a
;                            specific port;
;   '/path/to/unix/socket' - to listen on a unix socket.
; Note: This value is mandatory.
listen = 127.0.0.1:9000
listen = /run/php-fcgi.sock

sudo ll /run/php-fcgi.sock

Должен дать srw-rw-rw-.

Или как настроить на Debian/Ubuntu

Руководство:

источник: https://emi.is/?page=articles&article=php-7-installation-and-configuration-for-apache-2.4-using-php-fpm-(debian,-repository)


sudo apt purge 'php*' or sudo apt-get purge 'php*'
sudo add-apt-repository ppa:ondrej/php
sudo apt-get update
sudo apt install php7.3 php7.3-fpm php-mysql php-mbstring php-gd php-imap libapache2-mod-security2 modsecurity-crs
systemctl status php7.3-fpm
systemctl stop php7.3-fpm.service

sudo a2dismod php7.0 php7.1 php7.2 mpm_event mpm_worker
sudo a2enmod mpm_prefork
sudo a2enmod php7.3
sudo systemctl restart apache2 (httpd in CentOS)

Проблема в том, что php 7.3 из репозитория Ondrej работает только в режиме mpm_prefork. У него есть git repo, так что вы можете найти его в сети и спросить его, сделает ли он php 7.3 для mpm_worker и mpm_event. Остальная конфигурация для дистрибутивов семейства Debian приведена ниже:


sudo apt --assume-yes install php7.3-fpm
sudo systemctl stop php7.3-fpm.service
sudo rm /var/log/php7.0-fpm.log
sudo mkdir /var/log/php7.3-fpm/
sudo touch /var/log/php7.3-fpm/error.log
sudo mkdir /var/log/php7.3/
sudo touch /var/log/php7.3/error.log
sudo mkdir /var/tmp/php7.3/
sudo > /etc/php/7.3/fpm/php.ini
sudo > /etc/php/7.3/fpm/php-fpm.conf
sudo rm /etc/php/7.3/fpm/pool.d/www.conf
sudo touch /etc/php/7.3/fpm/pool.d/example.com.conf
sudo useradd --comment "PHP" --shell "/usr/sbin/nologin" --system --user-group php

sudo nano /etc/php/7.3/fpm/php.ini

вставить


[PHP]
date.timezone = Europe/Prague
display_errors = Off
error_log = /var/log/php7.3/error.log
error_reporting = 32767
log_errors = On
register_argc_argv = Off
session.gc_probability = 0
short_open_tag = Off
upload_tmp_dir = /var/tmp/php7.3/

sudo nano /etc/php/7.3/fpm/php-fpm.conf

вставить


[global]
error_log = /var/log/php7.3-fpm/error.log
include = /etc/php/7.3/fpm/pool.d/*.conf

sudo nano /etc/php/7.3/fpm/pool.d/example.com.conf

вставить


[example.com]
group = php
listen = 127.0.0.1:9000
pm = ondemand
pm.max_children = 5
pm.max_requests = 200
pm.process_idle_timeout = 10s
user = php

sudo nano /etc/logrotate.d/php7.3-fpm

скопируйте это в текстовый файл:

/var/log/php7.3-fpm.log {
    rotate 12
    weekly
    missingok
    notifempty
    compress
    delaycompress
    postrotate
            /usr/lib/php/php7.3-fpm-reopenlogs
    endscript
}

удалите его, а затем вставьте вместо этого:

/var/log/php7.3/*.log /var/log/php7.3-fpm/*.log
{
copytruncate
maxage 365
missingok
monthly
notifempty
rotate 12
}

Добавить директиву

sudo nano /etc/apache2/sites-available/example.com.conf


<VirtualHost *:80>
    ServerName www.example.com
    ServerAlias example.com
    ServerAdmin [email protected]
    DocumentRoot /var/www/html/example.com/public_html
    DirectoryIndex index.php index.htm index.html index.xht index.xhtml
    LogLevel info warn
    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined

    <FilesMatch "^\.ht">
    Require all denied
    </FilesMatch>

    <files readme.html>
    order allow,deny
    deny from all
    </files>

    RewriteEngine on
    RewriteCond %{SERVER_NAME} =example.com
    RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]
    ProxyPassMatch ^/(.*\.php(/.*)?)$ fcgi://127.0.0.1:9000/var/www/html/example.com/public_html

    <Directory /var/www/html/example.com/public_html>
        Options Indexes FollowSymLinks Includes IncludesNOEXEC SymLinksIfOwnerMatch
        AllowOverride None
    </Directory>
</VirtualHost>

Затем включите сайт:

sudo a2ensite /etc/apache2/sites-available/example.com.conf

Затем отредактируйте сайт SSL (в данном случае certbot из Let's Encrypt был установлен и настроен ранее в начале настройки SSL-сертификата).

sudo nano /etc/apache2/sites-available/example.com-le-ssl.conf

<IfModule mod_ssl.c>
    #headers for security man in the middle attack find how to enable this mod in Google
    LoadModule headers_module modules/mod_headers.so
    <VirtualHost *:443>
        Header always set Strict-Transport-Security "max-age=15768000"
        SSLEngine On
        ServerName example.com
        ServerAdmin [email protected]
        DocumentRoot /var/www/html/example.com/public_html
        <Directory /var/www/html/example.com/public_html>
        Options Indexes FollowSymLinks Includes IncludesNOEXEC SymLinksIfOwnerMatch
        AllowOverride All
        Require all granted
        DirectoryIndex index.php
        RewriteEngine On
         <FilesMatch ^/(.*\.php(/.*)?)$>
           SetHandler "fcgi://example.com:9000/var/www/html/example.com/public_html"
          </FilesMatch>
        </Directory>
    # Log file locations
    #LogLevel info ssl:warn
    LogLevel debug
    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined

    # modern configuration
    SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
    Include /etc/letsencrypt/options-ssl-apache.conf
    SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
    #SSLCipherSuite HIGH:!aNULL:!MD5
    SSLCipherSuite ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM$
    SSLHonorCipherOrder on
    SSLCompression off
    SSLSessionTickets off

    <FilesMatch "^\.ht">
    Require all denied
    </FilesMatch>

    <files readme.html>
       order allow,deny
       deny from all
    </files>

</VirtualHost>
    #Stapling OCSP for Let's Encrypt certs.
    SSLUseStapling          on
    SSLStaplingResponderTimeout     5
    SSLStaplingReturnResponderErrors        off
    SSLStaplingCache        shmcb:/var/run/ocsp(128000)
</IfModule>

sudo a2enmod proxy proxy_fcgi setenvif
sudo systemctl reload apache2.service
sudo chown --recursive root:adm /etc/php/
sudo chmod --recursive 0770 /etc/php/
sudo chown --recursive php:adm /var/log/php7.3/
sudo chown --recursive php:adm /var/log/php7.3-fpm/
sudo chmod --recursive 0770 /var/log/php7.3/
sudo chmod --recursive 0770 /var/log/php7.3-fpm/
sudo chown --recursive php:php /var/tmp/php7.3/
sudo chmod --recursive 0770 /var/tmp/php7.3/
sudo a2enconf php7.3-fpm
sudo systemctl enable php7.3-fpm.service
sudo systemctl start php7.3-fpm.service

Не забудьте добавить порт 9000 в брандмауэр в Debian/Ubuntu

sudo ufw allow 9000/tcp
sudo ufw status

On CentoOS / Fedora / Red Hat

sudo firewall-cmd --zone=public --add-port=9000/tcp --permanent
sudo firewall-cmd --reload
sudo firewall-cmd --list-all
sudo firewall-cmd --state 
Другие вопросы по тегам