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
Еще одно решение (требуется 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, это действительно плохое поведение.
Это то, что у меня есть. Кажется, работает нормально. Я поместил 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-прокси. Если бы у вас был отдельный пул для каждого пользователя, это позволило бы атаковать с повышением привилегий.
Немного измененная версия ответа @FrancescoA, не требующая mod_rewrite
<IfModule proxy_fcgi_module>
<FilesMatch \.php$>
<If "-f '%{REQUEST_FILENAME}'">
SetHandler "proxy:unix:/path/to/socket.sock|fcgi://unique-domain-name-string/"
</If>
</FilesMatch>
<Proxy fcgi://unique-domain-name-string>
ProxySet connectiontimeout=5 timeout=240
</Proxy>
</IfModule>
Одна вещь, с которой я столкнулся при решении этой проблемы, заключается в том, что если вы используете комбинацию:
chroot = /path/to/site
chdir = /
В вашей конфигурации пула fpm не передавайте полный путь к ProxyPass
директивы.
ProxyPass fcgi://127.0.0.1:9020/$1
Но, ТОЛЬКО, если пул на этом порте хромирован.
Лучший способ решить эту проблему - включить журналы отладки для 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>
Я не уверен, что проблема связана, но я нашел частично работающее решение здесь:
Уловка, кажется, добавляет? 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 "".
Надеюсь, этот маленький кусочек поможет кому-то решить свои проблемы.
Я сталкиваюсь с такими же проблемами на моем сервере (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 webmaster@localhost
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
Это работает с 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
Руководство:
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
Линоде имеет большой учебник на эту тему
В основном вы устанавливаете обработчик для всего сервера, который будет перехватывать любые php-скрипты и передавать их в fast-cgi.