Apache 2.4 + PHP-FPM + ProxyPassMatch


31

Di recente ho installato Apache 2.4 sul mio computer locale, insieme a PHP 5.4.8 usando PHP-FPM.

Tutto è andato abbastanza bene (dopo un po '...) ma c'è ancora uno strano errore:

Ho configurato Apache per PHP-FPM in questo modo:

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

Funziona, ad esempio se chiamo http://localhost/info.phpottengo il corretto phpinfo()(è solo un file di prova).

Se chiamo una directory comunque, ottengo un 404 con body File not found.e nel registro degli errori:

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

Aggiornare

Ora ho provato a fare il 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>

Ma il problema è: reindirizza sempre, perché on viene richiesto http://localhost/automaticamente http://localhost/index.php, a causa di

DirectoryIndex index.php index.html

Aggiornamento 2

Ok, quindi penso "forse controlla prima che ci sia un file da dare al proxy:

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

Ora la riscrittura completa non funziona più ...

Aggiornamento 3

Ora ho questa soluzione:

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

Prima controlla che ci sia un file da passare a PHP-FPM (con il percorso completo e assoluto ) e quindi esegui la riscrittura.

Questo non funziona quando si utilizza la riscrittura degli URL all'interno di una sottodirectory, inoltre non riesce per gli URL come http://localhost/index.php/test/ So back to square one.


Qualche idea?

Risposte:


34

Dopo ore di ricerca e lettura della documentazione di Apache, ho trovato una soluzione che consente di utilizzare il pool e di consentire alla direttiva Rewrite in .htaccess di funzionare anche quando l'URL contiene file .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>

Secondo la documentazione di Apache, il parametro proxy SetHandler richiede Apache HTTP Server 2.4.10.

Spero che questa soluzione sia di aiuto anche a te.


2
Questa è sicuramente la risposta per il 2015, tutto il resto qui è una schifezza per un setup moderno (diciamo debian stable)
Dmitri DB

1
Ho sbattuto la testa contro il muro su questo stesso problema per un po 'di tempo, e ho una configurazione estremamente simile alla tua. Pubblichereste per favore le vostre direttive di riscrittura .htaccess? Da quello che ho capito, tutto in questa risposta è solo quello che hai nel tuo file httpd.d / site.conf.
David W,

1
Al momento l'utilizzo di questa RewriteRule sembra abbastanza pericoloso in quanto potrebbe esporre i config.php file in chiaro se fossero all'interno di directory Alias ​​e quindi non esistenti in% {DOCUMENT_ROOT} /% {REQUEST_URI}.
Zulakis,

1
Incredibili 9 righe di codice. Questo è il Santo Graal e l'unica cosa che funziona al 100% per me. Solo un sidenote: se si passa da una soluzione utilizzando LocationMatch, non è necessario aggiungere il percorso del file assoluto all'URL FCC. Attiva il proxy e riscrivi la registrazione in apache per fare attenzione.
Phil

1
+1 perché questo post, diversamente da ogni altra risorsa che ho visto, mi ha aiutato a capire cosa dovrebbe rappresentare "unique-domain-name-string".
tre

10

Mi sono imbattuto anche in questo problema ieri - Apache 2.4 è passato da Debian / sperimentale a Debian / unstable costringendomi a gestire queste nuove cose; ovviamente non sui nostri server di produzione;).

Dopo aver letto quelli che sembrano milioni di siti, documenti Apache, segnalazioni di errori e output di debug nel registro degli errori, ho finalmente funzionato. No, non c'è ancora supporto per FPM con socket . La configurazione Debian predefinita utilizza i socket da un po 'di tempo, quindi anche gli utenti Debian dovranno cambiarlo.

Ecco cosa funziona per un sito CakePHP e PHPMyAdmin (quest'ultimo ha bisogno di una configurazione se si utilizzano i pacchetti Debian), quindi posso confermare che mod_rewritefunziona ancora come previsto per la riscrittura degli URL.

Nota DirectoryIndex index.php, che potrebbe essere il motivo per cui nessuna delle tue configurazioni ha funzionato per "cartelle" (almeno questo è ciò che non ha funzionato qui).

Ricevo ancora File not found.per le directory, ma solo se non esiste un file indice può analizzare. Mi piacerebbe liberarmene anche io, ma non è così critico come per ora.


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

Il vhost sopra funziona perfettamente con un .htaccess nella radice in questo modo:

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

Non capisco bene cosa intendi URL rewriting inside a subdirectory(sto solo riscrivendo nell'indice.php della radice).


(Oh, e dovrai assicurarti che Xdebug non sia in conflitto con FPM sul tuo sistema, per impostazione predefinita vogliono usare le stesse porte.)


Questa è una buona soluzione ma sfortunatamente questo approccio non funziona quando è necessario riscrivere gli URL contenenti .php, ad es. Per il sito multiplo di WordPress. /ms_blog_1/wp-admin/load-scripts.php?blah=blah
Phil

Per me, solo l'aggiunta di una sostituzione DirectoryIndex index.htmlnel vhost in questione lo ha risolto. Se ho DirectoryIndex index.php, sembra che altri file PHP finiscano per dare l'errore "File not found" e "Primary script unknown". Nel mio caso, ho un index.htmlfile ma un file php test.php.
geerlingguy,

4

Tutto quello che devi fare è impostare:

 ProxyErrorOverride on

E non dimenticare di impostare la pagina del cliente tramite:

ErrorDocument 404 /path/to/error_page_file    

2

Questo è quello che ho. Sembra funzionare bene. Ho messo Drupal in una sottodirectory e le sue riscritture funzionano, gli indici di directory funzionano e PATH_INFO funziona.

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

Ho provato a fare qualcosa del genere senza riscrivere ("If" e simili), ma non sono riuscito a far funzionare nulla.

EDIT: Nota che se dovessi implementarlo come provider di hosting condiviso, questo potrebbe essere un problema di sicurezza. Consentirebbe agli utenti di passare gli script PHP a un proxy fcgi arbitrario. Se avessi un pool separato per ogni utente, ciò consentirebbe un aumento degli attacchi con privilegi.


2

Ancora un'altra soluzione (richiede Apache> = 2.4.10) - All'interno 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>

Quindi qui, il gestore fcgi per PHP verrà impostato solo se il file esiste e se il suo nome corrisponde all'estensione del file PHP.

A proposito: per coloro che avrebbero idea di impostare ProxyErrorOverride su On , tenere presente che questa è davvero una cattiva idea. L'uso di questa direttiva non è senza causare alcun problema. Ad esempio, qualsiasi applicazione PHP che invia codice HTTP come 503 porterebbe a risultati imprevisti. Il gestore degli errori predefinito sarebbe coinvolto in ogni caso e per le applicazioni PHP che forniscono API, è davvero un comportamento scorretto.


Sfortunatamente si è verificato ancora l'errore "AH01071: Errore" Script primario sconosciuto \ n "" non riuscito che utilizza questa soluzione.
Klor,

1

Il modo migliore per risolvere questo problema è attivare i log di debug per mod_proxy e mod_rewrite e php-fpm. In apache 2.4 ora puoi attivare i log di debug solo per moduli specifici. http://httpd.apache.org/docs/current/mod/core.html#loglevel La configurazione per modulo e per directory è disponibile in Apache HTTP Server 2.3.6 e versioni successive

Forse stai ottenendo una doppia barra sulle directory?

Ecco cosa uso e funziona benissimo:

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

1

Una cosa che ho riscontrato nel trattare questo problema è che se usi la combinazione di:

chroot = /path/to/site
chdir = /

Nella configurazione del tuo pool di fpm, non passare l'intero percorso alla ProxyPassdirettiva.

ProxyPass fcgi://127.0.0.1:9020/$1

Ma -SOLO- se il pool su quella porta è chroot.


1

Non sono sicuro che il problema sia correlato, ma ho trovato una soluzione parzialmente funzionante qui:

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

Il trucco sembra aggiungere un? char in .htaccess RewriteRule, ad es. usando:

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

invece di:

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

La fonte del problema sembra essere una modifica in mod_rewrite di Apache 2.4.25. Ho usato il livello di log di Apache trace1 per osservare un "loop" che passa $ 1 a php-fpm dopo che è stato passato index.php / $ 1. $ 1 genera l'errore "AH01071: Errore ottenuto" Script primario sconosciuto \ n "".

Spero che questo piccolo bocconcino aiuti qualcuno a risolvere i propri problemi.



0

ho l'errore anche dopo il passaggio a php-fpm + apache 2.4.6 per le istanze di drupal

ma sto usando il mod evento mpm

basta inserire

DirectoryIndex index.php per me va bene

allora le mie impostazioni del Vhost sono come sotto

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

Grazie

non è necessario rivedere il file .htaccess predefinito di drupal


[Mer 25 Apr 01: 41: 31.526781 2018] [proxy_fcgi: error] [pid 2012: tid 140181155772160] (70007) Il timeout specificato è scaduto: [client 127.0.0.1:60308] AH01075: Errore nell'invio della richiesta a:, referer: www / admin / reports
sealionking

0

Devo affrontare gli stessi problemi sul mio server (centos 7.3.16 docker). Dopo aver tracciato il registro php-fpm, ho scoperto che mancava una lib di sistema. WARNING: [pool www] child 15081 said into stderr: "php-fpm: pool www: symbol lookup error: /lib64/libnsssysinit.so: undefined symbol: PR_GetEnvSecure" quindi, riavvio nspr, funziona. Se non riesci a trovare le soluzioni dopo aver provato alcuni metodi, puoi provare questo. yum -y install/reinstall nspr


0

Funziona con Wordpress 5.1.1 e versioni successive insieme a PHP 7.3, FastCGI, proxy, anche MariaDB / MySQL. Controllato due volte sui miei server. Funziona come un fascino.

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

Modifica questo file:

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

Incolla questo:

[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

Dovrebbe dare srw-rw-rw-.

O come impostare su Debian / Ubuntu

Tutorial:

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

Il problema è che php 7.3 dal repository Ondrej funziona solo con la modalità mpm_prefork. Ha git repo, quindi puoi trovarlo in rete e chiedergli, farà php 7.3 per mpm_worker e mpm_event. Il resto della configurazione per le distribuzioni della famiglia Debian è di seguito:


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

incolla


[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

incolla


[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

incolla


[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 questo nel file txt:

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

rimuoverlo e incollarlo anziché sopra:

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

Aggiungi direttiva

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>

Quindi abilitare il sito:

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

Prossima modifica del sito SSL (in questo caso certbot da Let's Encrypt è stato installato e configurato in precedenza all'inizio della configurazione del certificato 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

Ricorda di aggiungere una porta 9000 a un firewall su Debian / Ubuntu

sudo ufw allow 9000/tcp
sudo ufw status

Su 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 
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.