Apache 2.4 + PHP-FPM + ProxyPassMatch

31

Recientemente instalé Apache 2.4 en mi máquina local, junto con PHP 5.4.8 usando PHP-FPM.

Todo salió bastante bien (después de un tiempo ...) pero todavía hay un extraño error:

Configuré Apache para PHP-FPM de esta manera:

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

Funciona, por ejemplo, si llamo http://localhost/info.phpobtengo el correcto phpinfo()(es solo un archivo de prueba).

Sin embargo, si llamo a un directorio, obtengo un 404 con cuerpo File not found.y en el registro de errores:

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

Actualizar

Ahora intenté hacer el proxy con 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>

Pero el problema es: siempre está redirigiendo, porque se solicita http://localhost/automáticamente http://localhost/index.php, debido a

DirectoryIndex index.php index.html

Actualización 2

Ok, entonces pienso "tal vez verifique si hay un archivo para darle al proxy primero:

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

Ahora la reescritura completa ya no funciona ...

Actualización 3

Ahora tengo esta solución:

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

Primero verifique que haya un archivo para pasar a PHP-FPM (con la ruta completa y absoluta ) y luego realice la reescritura.

Esto no funciona cuando se usa la reescritura de URL dentro de un subdirectorio, también falla para URL como http://localhost/index.php/test/ So back to square one.


¿Algunas ideas?

apfelbox
fuente

Respuestas:

34

Después de horas de buscar y leer la documentación de Apache, he encontrado una solución que permite usar el grupo y también permite que la directiva Rewrite en .htaccess funcione incluso cuando la url contiene archivos .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>

Según la documentación de Apache, el parámetro proxy SetHandler requiere Apache HTTP Server 2.4.10.

Espero que esta solución también te ayude.

FrancescoA
fuente
2
Esta es definitivamente la respuesta para 2015, todo lo demás aquí es basura para una configuración moderna (digamos debian estable)
Dmitri DB
1
He estado golpeándome la cabeza contra la pared con este mismo problema durante bastante tiempo, y tengo una configuración extremadamente similar a la tuya. ¿Podría publicar sus directivas de reescritura .htaccess? Por lo que entiendo, todo en esta respuesta es solo lo que tiene en su archivo httpd.d / site.conf.
David W
1
Por el momento, el uso de esta RewriteRule parece bastante peligroso, ya que podría exponer config.php archivos sin formato si estuvieran dentro de directorios con alias y, por lo tanto, no existieran en% {DOCUMENT_ROOT} /% {REQUEST_URI}.
Zulakis
1
Increíbles 9 líneas de código. Este es el Santo Grial y lo único que funciona al 100% para mí. Solo una nota al margen: si está cambiando de una solución usando LocationMatch, no necesita agregar la ruta absoluta del archivo a la URL de fcgi. Encienda el proxy y vuelva a escribir el inicio de sesión en Apache para tener cuidado con esto.
Phil
1
+1 porque esta publicación, a diferencia de cualquier otro recurso que he visto, me ayudó a comprender qué se supone que representa "cadena-nombre-dominio-único".
tres
10

Ayer también me encontré con este problema: Apache 2.4 se mudó de Debian / experimental a Debian / inestable, lo que me obligó a lidiar con estas cosas nuevas; no en nuestros servidores de producción, por supuesto;).

Después de leer lo que parecen millones de sitios, documentos de Apache, informes de errores y resultados de depuración en el registro de errores, finalmente lo puse a trabajar. No, todavía no hay soporte para FPM con sockets . La configuración predeterminada de Debian ha estado usando sockets durante algún tiempo, por lo que los usuarios de Debian tendrán que cambiar eso también.

Esto es lo que funciona para un sitio CakePHP y PHPMyAdmin (este último necesita algo de configuración si está utilizando los paquetes de Debian), por lo que puedo confirmar que mod_rewritetodavía funciona como se espera para una reescritura de URL elegante.

Tenga en cuenta DirectoryIndex index.php, que podría ser la razón por la que ninguna de sus configuraciones funcionó para "carpetas" (al menos eso es lo que no funcionó aquí).

Todavía obtengo File not found.directorios, pero solo si no hay un archivo de índice se puede analizar. Me encantaría deshacerme de eso también, pero no es tan crítico como por ahora.


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

El vhost anterior funciona perfectamente bien con un .htaccess en la raíz como este:

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

Sin URL rewriting inside a subdirectoryembargo , no entiendo a qué te refieres (solo estoy reescribiendo en el index.php de la raíz).


(Ah, y tendrá que asegurarse de que Xdebug no entre en conflicto con FPM en su sistema, listos para usar, quieren usar los mismos puertos).

Alex
fuente
Esta es una buena solución, pero desafortunadamente este enfoque no funciona cuando las URL que contienen .php deben reescribirse, por ejemplo, para WordPress multisitio. /ms_blog_1/wp-admin/load-scripts.php?blah=blah
Phil
Para mí, solo agregar una anulación DirectoryIndex index.htmlen el vhost en cuestión lo solucionó. Si lo he hecho DirectoryIndex index.php, parece que otros archivos PHP terminan dando el error 'Archivo no encontrado' y 'Script primario desconocido'. En mi caso, tengo un index.htmlarchivo pero un php test.php.
geerlingguy
4

Todo lo que necesitas hacer es configurar:

 ProxyErrorOverride on

Y no olvide configurar la página del cliente:

ErrorDocument 404 /path/to/error_page_file    
Shiqi Zhong
fuente
2

Esto es lo que tengo. Parece funcionar bien. Puse Drupal en un subdirectorio y su reescritura funciona, los índices de directorio funcionan y PATH_INFO funciona.

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

Intenté hacer algo como esto sin reescribir ("If" y tal), pero no pude hacer que nada funcionara.

EDITAR: tenga en cuenta que si implementara esto como un proveedor de alojamiento compartido, esto podría ser un problema de seguridad. Permitiría a los usuarios pasar scripts PHP a un proxy fcgi arbitrario. Si tuviera un grupo separado para cada usuario, eso permitiría la elevación de los ataques de privilegios.

RockinRoel
fuente
2

Otra solución más (requiere Apache> = 2.4.10) - Dentro del 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>

Entonces, aquí, el controlador fcgi para PHP se establecerá solo si el archivo existe y si su nombre coincide con la extensión del archivo PHP.

Por cierto: para aquellos que tendrían la idea de establecer ProxyErrorOverride en On , tenga en cuenta que esta es realmente una mala idea. El uso de esta directiva no es sin causar ningún problema. Por ejemplo, cualquier aplicación PHP que envíe código HTTP como 503 conduciría a un resultado inesperado. El controlador de errores predeterminado estaría involucrado en cualquier caso y para las aplicaciones PHP que proporcionan API, eso es realmente un mal comportamiento.

Nuxwin
fuente
Lamentablemente, todavía tenía "AH01071: error obtenido 'Error primario de script desconocido \ n'" al usar esta solución.
klor
1

La mejor manera de resolver esto es activar los registros de depuración para mod_proxy y mod_rewrite y php-fpm. En apache 2.4 ahora puede activar los registros de depuración solo para módulos específicos. http://httpd.apache.org/docs/current/mod/core.html#loglevel La configuración por módulo y por directorio está disponible en Apache HTTP Server 2.3.6 y versiones posteriores

¿Quizás estás obteniendo una doble barra en los directorios?

Esto es lo que uso y funciona bien:

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

Una cosa que encontré al tratar este problema es que si usa la combinación de:

chroot = /path/to/site
chdir = /

En la configuración de su grupo fpm, no pase la ruta completa a la ProxyPassdirectiva.

ProxyPass fcgi://127.0.0.1:9020/$1

Pero -SÓLO- si el grupo en ese puerto está cortado.

hielo delgado
fuente
1

No estoy seguro de si el problema está relacionado, pero he encontrado una solución que funciona parcialmente aquí:

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

El truco parece ser agregar un? char en .htaccess RewriteRule, por ejemplo, usando:

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

en lugar de:

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

La fuente del problema parece ser un cambio en mod_rewrite de Apache 2.4.25. He usado el nivel de registro Apache trace1 para observar un "ciclo" que pasa $ 1 a php-fpm después de que se haya pasado index.php / $ 1. Los $ 1 generan el error "AH01071: Error obtenido 'Secuencia de comandos primaria desconocida \ n'".

Espero que este pequeño dato ayude a alguien a resolver sus problemas.

Biapy
fuente
0

Tengo el error también después de cambiar a php-fpm + apache 2.4.6 para instancias de drupal

pero estoy usando mod de eventos mpm

solo inserta

DirectoryIndex index.php funciona para mi

entonces mi configuración de Vhost se ve a continuación

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

Gracias

no es necesario revisar el archivo .htaccess predeterminado de drupal

sello marino
fuente
[Mié 25 de abril 01: 41: 31.526781 2018] [proxy_fcgi: error] [pid 2012: tid 140181155772160] (70007) El tiempo de espera especificado ha expirado: [cliente 127.0.0.1:60308] AH01075: Error al enviar la solicitud a:, árbitro: www / admin / reports
sealionking
0

Me enfrento a los mismos problemas en mi servidor (centos 7.3.16 docker). Después de rastrear el registro php-fpm, encontré 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" luego, vuelvo a instalar el nspr, funciona. Si no puede encontrar las soluciones después de probar algún método, puede intentarlo. yum -y install/reinstall nspr

limón
fuente
0

Esto funciona con Wordpress 5.1.1 y más reciente junto con PHP 7.3, FastCGI, proxy, también MariaDB / MySQL. Comprobado dos veces en mis servidores. Funciona de maravilla.

Primero en 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 archivo:

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

Pega esto:

[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

Debería dar srw-rw-rw-.

O cómo configurar en Debian / Ubuntu

Tutorial:

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

El problema es que php 7.3 del repositorio de Ondrej solo funciona con el modo mpm_prefork. Tiene git repo, por lo que puede encontrarlo en la red y preguntarle si creará php 7.3 para mpm_worker y mpm_event. El resto de la configuración para las distribuciones de la familia Debian está a continuación:


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

pegar


[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

pegar


[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

pegar


[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

copia esto al archivo txt:

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

eliminarlo y luego pegar esto en lugar de arriba:

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

Agregar directiva

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>

Luego habilite el sitio:

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

A continuación, edite el sitio SSL (en este caso, certbot de Let's Encrypt se instaló y configuró previamente al comienzo de la configuración del 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 [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

Recuerde agregar un puerto 9000 a un firewall en Debian / Ubuntu

sudo ufw allow 9000/tcp
sudo ufw status

En 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 
Gall Anonim
fuente