Apache 2.4 + PHP-FPM + ProxyPassMatch

I recently installed Apache 2.4 on my local machine, together with PHP 5.4.8 using PHP-FPM.

Everything went quite smoothly (after a while...) but there is still a strange error:

I configured Apache for PHP-FPM like this:

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

It works, for example if I call http://localhost/info.php I get the correct phpinfo() (it is just a test file).

If I call a directory however, I get a 404 with body File not found. and in the error log:

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

Update

I now tried doing the proxying with 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>

But the problem is: it is always redirecting, because on http://localhost/ automatically http://localhost/index.php is requested, because of

DirectoryIndex index.php index.html

Update 2

Ok, so I think "maybe check whether there is a file to give to the proxy first:

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

Now the complete rewriting does not work anymore...

Update 3

Now I have this solution:

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

First check, that there is a file to pass to PHP-FPM (with the full and absolute path) and then do the rewriting.

This does not work when using URL rewriting inside a subdirectory, also it fails for URLs like http://localhost/index.php/test/ So back to square one.


Any ideas?


Solution 1:

After hours of searching and reading Apache documentation I've come up with a solution that allows to use the pool, and also allow the Rewrite directive in .htaccess to work even when the url contains .php files.

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

As per Apache documentation, the SetHandler proxy parameter requires Apache HTTP Server 2.4.10.

I hope that this solution will help you too.

Solution 2:

I ran into this problem yesterday as well – Apache 2.4 moved out of Debian/experimental into Debian/unstable forcing me to deal with this new stuff; not on our production servers of course ;).

After reading what feels like millions of sites, Apache docs, bug reports and debugging output in the error log I finally got it to work. No, there's no support for FPM with sockets, yet. The default Debian config has been using sockets for some time now, so Debian users will have to change that too.

Here's what works for a CakePHP site and PHPMyAdmin (the latter needs some config if you're using the Debian packages though), so I can confirm that mod_rewrite still works as expected to do fancy URL rewriting.

Notice DirectoryIndex index.php, which might be the reason none of your configs worked for "folders" (at least that's what didn't work here).

I still get File not found. for directories, but only if there's no index file it can parse. Would love to get rid of that too, but it's not that critical as for now.


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

The above vhost works perfectly well with an .htaccess in the root like this:

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

I don't quite get what you mean by URL rewriting inside a subdirectory though (I'm only rewriting to the root's index.php).


(Oh, and you'll have to make sure Xdebug doesn't conflict with FPM on your system, out of the box they want to use the same ports.)

Solution 3:

All you need to do is to set:

 ProxyErrorOverride on

And do not forget to set the customer page by:

ErrorDocument 404 /path/to/error_page_file    

Solution 4:

Yet another solution (requires Apache >= 2.4.10) - Inside the 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>

So here, the fcgi handler for PHP will be set only if the file exists and if its name matches with PHP file extension.

BTW: For those which would have idea to set the ProxyErrorOverride to On, be aware that this is really a bad idea. Usage of this directive is not without causing any issue. For instance, any PHP application sending HTTP code such as 503 would lead to unexpected result. Default error handler would be involved in any cases and for PHP applications that provide API, that is really a bad behavior.