There are a number of guides out there on how someone has gotten their web server to work with PHP-FPM. Most of them are Nginx, but a few are Apache. While these articles will happily show you their configuration files, I haven’t found any that actually give formulas for the configuration files and why they have to be set up the way they are. The configuration for Apache’s handlers, actions, aliases, scriptaliases (if you use them), and mod_fastcgi are complicated and very much unclear. I imagine that the authors of these helpful blog posts were so happy to be done getting their configurations working that they set the files read-only and immutable, copied and pasted into their blogs, and filed it away next to the times they wrote xorg.conf and zone files from scratch.
Indeed, that’s how I wanted it to be for me. Life has other plans.
It’s not OK, in this day and age, to run all the sites on a server as the same user. You must run different sites as different users, because they’re probably PHP, and PHP gets hacked. All. The. Time. (And another for good measure. There are plenty more where that came from.) So the setup I came up with had to support multiple users. This is more complicated than a single-site setup; there are shortcuts you can get away with there, that you can’t get away with in a multi-user setup.
The reason this is all so annoying and complicated is because there are so many layers of indirection between an Apache request for a dynamic page, and the actual PHP process. It’s ridiculous:
- Dynamic page handlers are determined by a MIME type (application/x-httpd-php) being assigned to an extension (.php).
- The instruction on how to handle that MIME type is controlled by an Action directive, which points to an executable.
- That executable is actually not a real thing: it’s just a filename that we make up, which then gets Aliased to a different executable someplace in the filesystem.
- However, that executable isn’t a real thing either; it’s another file that doesn’t exist. FastCGI knows what it is, though, and passes that request to a socket where PHP-FPM is listening.
- Once the request passes through the socket, the FPM master process decides which child process is going to handle it, or whether to spawn a new child process for it, and sends it where it’s going.
- After this arduous journey, the PHP process finally gets to process the request and spit lovely HTML data back to Apache, which serves it back to the browser.
And the configuration for the levels of indirection live in different places. The first three live in the virtualhost configuration:
AddType application/x-httpd-php .php Action application/x-httpd-php /php.fcgi Alias /php.fcgi /dev/shm/david-php.fcgi
You can see the MIME type being created and assigned for use with .php files. The Action line tells Apache what to do with that MIME type: send it to the php.fcgi “executable”. The Alias line tells Apache /php.fcgi is really /dev/shm/user-php.fcgi. (It isn’t really that, either, but it’s getting closer.) “David” is a placeholder for the name of the user who will ‘own’ this virtualhost and all its files. Now, in the configuration file where mod_fastcgi is loaded (httpd.conf, or /etc/apache2/mods-available/fastcgi.conf, or possibly someplace else) you need one of this line for every user that will own one or more vhosts:
FastCGIExternalServer /dev/shm/david-php.fcgi -socket /var/run/user.sock
Again, david is a placeholder for the name of the user who owns the vhost. This line is the last level of indirection that Apache deals with: the pretend binary that the Alias directive above points to in /dev/shm is defined here. (Using /dev/shm/ is arbitrary; /var/run also works. It might work if the path doesn’t exist at all; I’m not sure. It works now and I don’t want to touch it anymore, y’know?) Requests will be sent here, and FastCGI will then relay them to the socket where PHP-FPM is listening. Speaking of which, you need a pool definition file that includes that. Here’s an example:
[david] user = david group = david listen = /var/run/david.sock listen.owner = www-data listen.group = www-data listen.mode = 0660 pm = dynamic pm.max_children = 5 pm.start_servers = 1 pm.min_spare_servers = 1 pm.max_spare_servers = 2 pm.max_requests = 32768 ;pm.status_path = /fpm-status
User and group need to match the username that owns the virtualhost and all its files. The listen variable contains the location of the socket through which Apache and PHP will communicate. The directory needs to exist, and the user as which PHP-FPM’s master process runs, needs to be able to create files there. PHP will create the socket; no need to worry about that.
- AddType, Action and Alias go in the virtualhost configuration file.
- FastCGIExternalServer goes in the module configuration file, or the main configuration file, after the module’s been loaded.
- listen goes in the pool definition file.
- The MIME type has to match between AddType and Action. The extension, of course, needs to be .php unless you’ve named your PHP files something weird.
- The second argument to Action must match the first argument to Alias, and it needs to be a file that doesn’t exist in the virtualhost.
- The second argument to Alias must match the first argument to FastCGIExternalServer, and needs to be a file that doesn’t exist in the filesystem. It also has to be unique per user.
- The second argument to FastCGIExternalServer has to match the listen directive in the pool definition file. This also has to be unique per user.
- The socket needs to be located in a directory where PHP-FPM’s master process can create files.
- The user defined in the pool definition file needs to own all the files in the virtualhost. Directories mode 755, files mode 644.
It’s a long list, and anything out of place will leave you with errors that are less-than-helpful to try to figure out. I sincerely hope that this guide will make the configuration of PHP-FPM with Apache easier for everyone who tries it.