Apache 2.4 + PHP-FPM + ProxyPassMatch


31

Eu instalei recentemente o Apache 2.4 na minha máquina local, junto com o PHP 5.4.8 usando o PHP-FPM.

Tudo correu bem (depois de um tempo ...), mas ainda existe um erro estranho:

Eu configurei o Apache para PHP-FPM assim:

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

Funciona, por exemplo, se eu ligar http://localhost/info.php, recebo o correto phpinfo()(é apenas um arquivo de teste).

Se eu chamar um diretório, no entanto, recebo um 404 com corpo File not found.e no log de erros:

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

Atualizar

Agora eu tentei fazer o proxy com 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>

Mas o problema é: ele está sempre redirecionando, porque é solicitado http://localhost/automaticamente http://localhost/index.php, por causa de

DirectoryIndex index.php index.html

Atualização 2

Ok, então acho que "talvez verifique se há um arquivo para fornecer ao proxy primeiro:

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

Agora a reescrita completa não funciona mais ...

Atualização 3

Agora eu tenho esta solução:

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

Primeiro verifique se há um arquivo para passar ao PHP-FPM (com o caminho completo e absoluto ) e depois faça a reescrita.

Isso não funciona ao usar a reescrita de URL dentro de um subdiretório, mas também falha em URLs como http://localhost/index.php/test/ So, de volta à estaca zero.


Alguma ideia?

Respostas:


32

Depois de horas pesquisando e lendo a documentação do Apache, encontrei uma solução que permite usar o pool e também permite que a diretiva Rewrite no .htaccess funcione mesmo quando o URL contém arquivos .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>

De acordo com a documentação do Apache, o parâmetro do proxy SetHandler requer o Apache HTTP Server 2.4.10.

Espero que esta solução também o ajude.


2
Esta é definitivamente a resposta para 2015, tudo aqui é uma porcaria para uma configuração moderna (Digamos debian stable)
Dmitri DB

11
Estou batendo a cabeça contra a parede nesse mesmo problema há algum tempo e tenho uma configuração extremamente semelhante à sua. Você poderia postar suas diretivas .htaccess Rewrite? Pelo que entendi, tudo nesta resposta é apenas o que você tem no seu arquivo httpd.d / site.conf.
David W

11
No momento, o uso desse RewriteRule parece bastante perigoso, pois pode expor os config.php arquivos sem formatação se eles estiverem dentro de diretórios com alias e, portanto, não existem em% {DOCUMENT_ROOT} /% {REQUEST_URI}.
Zulakis

11
Incríveis 9 linhas de código. Este é o Santo Graal e a única coisa que funciona 100% para mim. Apenas uma nota de rodapé: se você estiver alternando de uma solução usando o LocationMatch, não precisará anexar o caminho absoluto do arquivo ao URL fcgi. Ative o proxy e reescreva o logon no apache para observar isso.
8166 Phil

11
+1 porque esta postagem, diferente de qualquer outro recurso que eu já vi, me ajudou a entender o que "string de nome de domínio exclusivo" deve representar.
Threeve 5/04

10

Também encontrei esse problema ontem - o Apache 2.4 saiu do Debian / experimental para o Debian / instável, forçando-me a lidar com esse novo material; não em nossos servidores de produção, é claro;).

Depois de ler o que parece ser milhões de sites, documentos do Apache, relatórios de erros e saída de depuração no log de erros, finalmente consegui que funcionasse. Não, ainda não há suporte para o FPM com soquetes . A configuração padrão do Debian já usa soquetes há algum tempo, então os usuários do Debian também terão que mudar isso.

Aqui está o que funciona para um site CakePHP e para o PHPMyAdmin (o último precisa de alguma configuração se você estiver usando os pacotes Debian), para que eu possa confirmar que mod_rewriteainda funciona como esperado para reescrever URLs sofisticadas.

Observe DirectoryIndex index.phpque pode ser o motivo pelo qual nenhuma de suas configurações funcionou para "pastas" (pelo menos foi o que não funcionou aqui).

Ainda obtenho File not found.diretórios, mas somente se não houver um arquivo de índice ele poderá analisar. Adoraria se livrar disso também, mas não é tão crítico quanto agora.


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

O vhost acima funciona perfeitamente bem com um .htaccess na raiz assim:

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

Mas não entendi bem o que você quer dizer URL rewriting inside a subdirectory(estou reescrevendo apenas o index.php da raiz).


(Ah, e você terá que garantir que o Xdebug não entre em conflito com o FPM no seu sistema, pronto para usar as mesmas portas.)


Esta é uma boa solução, mas infelizmente essa abordagem não funciona quando os URLs que contêm .php precisam ser reescritos, por exemplo, para o multisite do WordPress. /ms_blog_1/wp-admin/load-scripts.php?blah=blah
Phil

Para mim, basta adicionar uma substituição DirectoryIndex index.htmlno vhost em questão corrigido. Se eu tiver DirectoryIndex index.php, parece que outros arquivos PHP acabam dando o erro 'Arquivo não encontrado' e 'Script primário desconhecido'. No meu caso, eu tenho index.htmlapenas um arquivo php test.php.
precisa saber é o seguinte

4

Tudo que você precisa fazer é definir:

 ProxyErrorOverride on

E não se esqueça de definir a página do cliente:

ErrorDocument 404 /path/to/error_page_file    

2

É isso que eu tenho. Parece funcionar bem. Coloquei o Drupal em um subdiretório e ele reescreve o trabalho, os índices de diretório funcionam e o PATH_INFO funciona.

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

Eu tentei fazer algo assim sem reescrever ("If" e tal), mas não consegui fazer nada funcionar.

EDIT: Observe que, se você implementasse isso como um provedor de hospedagem compartilhada, isso poderia ser um problema de segurança. Isso permitiria que os usuários passassem scripts PHP para um proxy fcgi arbitrário. Se você tivesse um pool separado para cada usuário, isso permitiria ataques de elevação de privilégios.


2

Outra solução (requer Apache> = 2.4.10) - Dentro do 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>

Portanto, aqui, o manipulador fcgi para PHP será definido apenas se o arquivo existir e se seu nome corresponder à extensão do arquivo PHP.

BTW: Para aqueles que teriam idéia de definir o ProxyErrorOverride como Ativado , saiba que essa é realmente uma péssima idéia. O uso desta diretiva não ocorre sem causar nenhum problema. Por exemplo, qualquer aplicativo PHP que envie código HTTP como 503 levaria a resultados inesperados. O manipulador de erros padrão estaria envolvido em todos os casos e, para aplicativos PHP que fornecem API, esse é realmente um mau comportamento.


Infelizmente ainda tinha o erro "AH01071: obteve o erro 'Script primário desconhecido \ n'" usando esta solução.
klor

1

A melhor maneira de resolver isso é ativar os logs de depuração para mod_proxy e mod_rewrite e php-fpm. No apache 2.4, agora você pode ativar os logs de depuração apenas para módulos específicos. http://httpd.apache.org/docs/current/mod/core.html#loglevel A configuração por módulo e por diretório está disponível no Apache HTTP Server 2.3.6 e posterior

Talvez você esteja recebendo uma barra dupla nos diretórios?

Aqui está o que eu uso e funciona bem:

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

1

Uma coisa que me deparei ao lidar com esse problema é que, se você usar a combinação de:

chroot = /path/to/site
chdir = /

Na configuração do seu pool de fpm, não passe o caminho completo para a ProxyPassdiretiva.

ProxyPass fcgi://127.0.0.1:9020/$1

Mas -SOMENTE- se o pool nessa porta estiver com chroot.


1

Não tenho certeza se o problema está relacionado, mas encontrei uma solução que funciona parcialmente aqui:

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

O truque parece estar adicionando um? char no .htaccess RewriteRule, por exemplo, usando:

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

ao invés de:

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

A fonte do problema parece ser uma alteração no mod_rewrite do Apache 2.4.25. Eu usei o nível de log Apache trace1 para observar um "loop" que passa $ 1 para php-fpm após a indexação de index.php / $ 1. O $ 1 gera o erro "AH01071: Erro obtido 'script primário desconhecido \ n'".

Espero que este pequeno detalhe ajude alguém a resolver seus problemas.



0

Eu tenho o erro também depois de mudar para php-fpm + apache 2.4.6 para instâncias drupal

mas estou usando o mod de evento mpm

basta inserir

DirectoryIndex index.php funciona para mim

então minhas configurações de Vhost se parecem abaixo

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

obrigado

não é necessário revisar o arquivo .htaccess padrão do drupal


[Wed Apr 25 01: 41: 31.526781 2018] [proxy_fcgi: error] [pid 2012: tid 140181155772160] (70007) O tempo limite especificado expirou: [client 127.0.0.1:60308] AH01075: Erro ao enviar solicitação para:, referenciador: www / admin / reports
sealionking

0

Eu enfrento os mesmos problemas no meu servidor (docker centos 7.3.16). Depois de rastrear o log php-fpm, encontrei miss a sys lib. WARNING: [pool www] child 15081 said into stderr: "php-fpm: pool www: symbol lookup error: /lib64/libnsssysinit.so: undefined symbol: PR_GetEnvSecure" Se você não encontrar as soluções depois de tentar qualquer método, poderá tentar isso. yum -y install/reinstall nspr


0

Isso funciona com o Wordpress 5.1.1 e mais recente, juntamente com o PHP 7.3, FastCGI, proxy, também MariaDB / MySQL. Verificado duas vezes nos meus servidores. Funciona como um encanto.

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

Edite este arquivo:

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

Cole isto:

[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

Deve dar srw-rw-rw-.

Ou como configurar no Debian / Ubuntu

Tutorial:

source: 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)

O problema é que o php 7.3 do Ondrej repo funciona apenas com o modo mpm_prefork. Ele tem git repo, então você pode encontrá-lo na net e perguntar se ele fará o php 7.3 para mpm_worker e mpm_event. O restante da configuração para as distribuições da família Debian está abaixo:


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

colar


[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

colar


[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

colar


[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

copie isso para o arquivo txt:

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

remova-o e cole-o em vez de acima:

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

Adicionar diretiva

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>

Em seguida, ative o site:

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

Próxima edição do site SSL (neste caso, o certbot de Let's Encrypt foi instalado e configurado anteriormente no início da configuração do certificado 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 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

Lembre-se de adicionar uma porta 9000 a um firewall no Debian / Ubuntu

sudo ufw allow 9000/tcp
sudo ufw status

No 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 
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.