PHP FastCGI Process Manager (or PHP-FPM for short) is an daemon that remains resident in memory and processes requests relayed to it either using a local domain socket (preferred) or a TCP socket (not preferred but sometimes the only option). In order to support this side loading/executing of PHP scripts it uses its own protocol which (as the name implies) is based on regular CGI behavior.
Contents
- What is FastCGI?
- Installing and Running PHP-FPM
- Configuring nginx For PHP-FPM
- Considerations with FastCGI
- Further Reading
What is FastCGI?
This FastCGI option stands in opposition to the two other main means of deploying the PHP interpreter: CGI and as an embedded server module. These other two options have a great number of performance drawbacks. CGI requires an executable be called on each and every attempt to interpret a PHP script. Embedding a PHP interpreter in the web server does save this invocation time but also results in immense overhead now that each worker process carries with it another interpreter even if the request currently being served is just for CSS or a PNG.
Considering these drawbacks it’s considered better to merge the already-in-memory-and-running benefits of a server module with the only-for-related items behavior of CGI. For this reason the current standard seems to be PHP-FPM where its worker processes can scale up as needed but only for PHP-related work.
nginx only supports the use of FastCGI so if you’re using that web server your decision has already been made there.
Installing and Running PHP-FPM
On ubuntu-based system:
root@f4ef9153e9b1:/# apt-get install -y php-fpm
Which makes the php-fpm7.0
executable available as well as the upstart service of the same name.
On CentOS-based system:
[root@4cdafe4fd91d /]# yum install -y php-fpm
Which makes the php-fpm
executable available as well as the systemd unit of the same name.
Deployment will consist of either executing the given executable directly for containers (on Ubuntu, you must give the -F
option to keep it from daemonizing) or configuring the respective service to start on boot for VM’s and physical installs.
For Ubuntu, the default behavior is for PHP-FPM to listen over a Unix domain socket, which I would recommend that you leave it at unless you have a particular reason to tell it to use TCP. On CentOS for reasons only Red Hat understands, TCP (and not Unix domain) is the default. For CentOS you’ll want to open and edit the [www]
section such that the listen directive is set to a Unix domain socket:
listen = /run/php/php7.0-fpm.sock;
and restart PHP-FPM if already running. In either case you’ll need to make sure the mode on the domain socket is readable by setting a good value to listen.mode
for example listen.mode = 0777
will leave permissions wide open (alright for personal development but probably not ideal for production services.
PHP-FPM itself has some tunables and configuration options but I’ll leave that for a later article as this one is strictly about getting PHP-FPM and nginx working together.
Configuring nginx For PHP-FPM
Now we get to the part we’ve all been waiting for. You have your existing nginx server and want to configure it to delegate execution of PHP scripts over to PHP-FPM. With nginx there are no special modules to load, all the web server needs to understand is the FastCGI protocol which is loaded as part of the regular nginx startup.
All that’s left, once there’s a FastCGI daemon to point at, is to direct nginx to hand PHP requests off to it. To do that we go to the relevant server{}
block for the virtual host we’re wanting to configure and add a new location{}
block. For example:
location ~ \.php$ { include fastcgi.conf; fastcgi_pass unix:/run/php/php7.0-fpm.sock; }
This stanza is pretty straight forward:
- The criteria for our
location
is a regexp pattern (~
) that matches any filename that ends in a literal.php
extension. - We include the default FastCGI parameters (equivalent to environmental variables in regular CGI) as specified in
/etc/nginx/fastcgi.conf
which does a good job of specifying what a standard configuration. - Finally, we instruct the FastCGI module in nginx to pass the request off to the process listening on the specified socket.
At this point you should be able to reload your server configuration with a nginx -t
(to validate syntax) and then a nginx -s reload
(to perform the actual reload).
Your PHP-FPM configuration should now be functional at a bare minimal level now. To test you may try to create a phpinfo();
script inside of the virtual host’s document root and accessing it by way of a web browser. Depending on the application you’re running you may need to do more but the above is sufficient just to get nginx and PHP-FPM communicating properly.
Considerations With FastCGI
In addition to the above, you may need to tweak nginx to work on your given system. Some key points to bear in mind:
- Exempt the location(s) of any upload directories your application has. This prevents someone from uploading a PHP file and executing it. Ideally your application should filter out these sorts of files but the modern standard is “defense in depth” so you should have a fail safe in your nginx configuration. For example, of a Drupal site you might change the
location{}
block so that it reads:location ~ \.php$ { include fastcgi.conf; if ($uri !~ /sites/default/files){ fastcgi_pass unix:/run/php/php7.0-fpm.sock; } }
- It may be useful to set HTTP_PROXY to an empty string (if you don’t need a value) to avoid a class of attacks where application and/or library developers put too much faith in
$_SERVER
values. This sets it to a static value before passing control over to PHP. This prevents the client from providing a value for this header. - If you application actually uses
PATH_INFO
1 (for example neither Drupal or WordPress do) then you’ll need to set fastcgi_split_path_info so that the value forPATH_INFO
can be separated from the file path in the URI. To figure out if you need that, aPATH_INFO
would be a URL such as/index.php/index/list
where theindex.php
portion is the script to be ran and/index/list
is some resource identifier that that application uses. If you don’t see that during your application’s normal functioning, then it’s best to leave it alone so that the path in the URL doesn’t get changed unnecessarily.
Further Reading
- (rascaldev) Tuning PHP’s FastCGI Process Manager (PHP-FPM)
- (rascaldev) Configuring Apache httpd for PHP-FPM
Citations
- RFC 3875 — Common Gateway Interface spec Section 4.1.5 [↩]