Apache 2.4 + PHP-FPM + ProxyPassMatch


31

我最近在本地计算机上安装了Apache 2.4,以及使用PHP-FPM的PHP 5.4.8。

一切都进行得很顺利(过了一会儿...),但是仍然有一个奇怪的错误:

我像这样为PHP-FPM配置了Apache:

<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()(它只是一个测试文件)。

但是,如果我调用目录,则会File not found.在错误日志中显示带有正文的404 :

[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重写时,此方法不起作用,对于诸如http://localhost/index.php/test/ So之类的URL,它也会失败。


有任何想法吗?

Answers:


34

经过数小时的搜索和阅读Apache文档,我提供了一个解决方案,该解决方案可以使用池,并且即使url中包含.php文件,也可以使.htaccess中的Rewrite指令起作用。

<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代理参数需要Apache HTTP Server 2.4.10。

我希望该解决方案也能为您提供帮助。


2
这绝对是2015年的答案,这里的所有其他内容都是现代设置(比如说debian稳定版)的废话
Dmitri DB

1
在这个问题上,我已经将头撞墙了很长时间,而且我的设置与您的设置极为相似。您能否发布.htaccess重写指令?据我了解,此答案中的所有内容仅是您在httpd.d / site.conf文件中拥有的内容。
David W

1
目前,使用此RewriteRule似乎非常危险,因为config.php 如果文件位于Aliased目录中并且因此在%{DOCUMENT_ROOT} /%{REQUEST_URI}中不存在,则文件可能会以纯格式公开。
Zulakis

1
惊人的9行代码。这是圣杯,对我来说唯一有效的方法是100%。只是一个旁注:如果您要使用LocationMatch从解决方案切换,则无需将绝对文件路径附加到fcgi url。打开代理并重写Apache中的日志记录以注意这一点。
Phil

1
+1是因为这篇帖子与我见过的其他所有资源都不一样,可以帮助我理解“唯一域名字符串”应该代表什么。
threeve

10

昨天我也遇到了这个问题– Apache 2.4从Debian / experimental迁移到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>

上面的虚拟主机与根目录中的.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冲突,开箱即用,他们想使用相同的端口。)


这是一个很好的解决方案,但不幸的是,当需要重写包含.php的url时,例如对于WordPress多站点,此方法不起作用。/ms_blog_1/wp-admin/load-scripts.php?blah=blah
Phil

对我来说,只需DirectoryIndex index.html在有问题的虚拟主机中添加替代项即可解决该问题。如果我有DirectoryIndex index.php,则其他PHP文件似乎最终出现“找不到文件”和“主脚本未知”错误。就我而言,我有index.html一个php文件test.php
geerlingguy

4

您需要做的只是设置:

 ProxyErrorOverride on

并且不要忘记通过以下方式设置客户页面:

ErrorDocument 404 /path/to/error_page_file    

2

这就是我所拥有的。看来工作正常。我将Drupal放在一个子目录中,它重写工作,目录索引工作和PATH_INFO工作。

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

我试图做这样的事情而无需重写(“ If”之类),但是我什么也做不了。

编辑:请注意,如果您要将其实现为共享托管提供程序,则可能是安全问题。它将允许用户将PHP脚本传递给任意的fcgi代理。如果每个用户都有一个单独的池,则可以进行特权提升攻击。


2

另一个解决方案(需要Apache> = 2.4.10)-在虚拟主机内部:

# 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>

因此,在这里,仅当文件存在且文件名与PHP文件扩展名匹配时,才会设置PHP的fcgi处理程序。

顺便说一句:对于那些想将ProxyErrorOverride设置为On的用户,请注意,这确实是一个坏主意。使用此伪指令并非没有问题。例如,任何发送HTTP代码(例如503)的PHP应用程序都将导致意外结果。在任何情况下,对于提供API的PHP应用程序,都会涉及默认错误处理程序,这的确是一种不良行为。


不幸的是,使用此解决方案仍然出现“ AH01071:出现错误'主脚本未知\ n'”错误。
klor

1

解决此问题的最佳方法是打开mod_proxy,mod_rewrite和php-fpm的调试日志。现在,在apache 2.4中,您仅可以打开特定模块的调试日志。 http://httpd.apache.org/docs/current/mod/core.html#loglevel Apache HTTP Server 2.3.6和更高版本中提供了按模块和按目录配置

也许在目录上出现双斜线?

这是我使用的,它工作正常:

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

1

我在处理此问题时遇到的一件事是,如果使用以下组合:

chroot = /path/to/site
chdir = /

在fpm池配置中,不要将完整路径传递给ProxyPass指令。

ProxyPass fcgi://127.0.0.1:9020/$1

但是-仅-如果该端口上的池是chroot。


1

我不确定是否与问题有关,但是我在这里找到了部分可行的解决方案:

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

诀窍似乎是增加一个?.htaccess RewriteRule中的char,例如,使用:

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

代替:

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

问题的根源似乎是Apache 2.4.25的mod_rewrite中的更改。我已经使用Apache trace1日志级别观察了一个“循环”,该循环在将index.php / $ 1通过后将$ 1传递给php-fpm。$ 1生成“ AH01071:错误'Primary script unknown \ n'”错误。

希望这个小窍门能帮助某人解决他们的问题。



0

在Drupal实例切换到php-fpm + apache 2.4.6之后,我也出现了错误

但是我正在使用mpm event mod

只需插入

DirectoryIndex index.php 为我工作

然后我的虚拟主机设置如下所示

<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>

谢谢

无需修改drupal的默认.htaccess文件


[2018年4月25日星期三01:41:31.526781] [proxy_fcgi:error] [pid 2012:tid 140181155772160](70007)指定的超时时间已到期:[客户端127.0.0.1:60308] AH01075:错误地将请求分发给:,引用者:www / admin / reports
sealionking

0

我在服务器上遇到同样的问题(centos 7.3.16 docker)。跟踪php-fpm日志后,我发现缺少sys lib。 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


0

这适用于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)

问题是Ondrej回购中的php 7.3仅适用于mpm_prefork模式。他有git repo,因此您可以在网上找到他并询问他,他会为mpm_worker和mpm_event制作php 7.3吗。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

将此复制到txt文件:

/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 admin@example.com
    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站点(在这种情况下,之前在SSL证书配置的开头已安装并配置了Let's Encrypt的certbot)。

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 admin@example.com
        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

在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 
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.