This is my OLD blog. Thew new one can be found here

PHP, apache and fastcgi – a comprehensive overview

I recently ran into trouble with our mod_fastcgi + PHP setup and had to to a lot of research to figure out what happened and how to fix it. This post summarizes the results of this research. It covers anything you might want to know about PHP + Apache and FastCGI, what modules there are, how you use them, what is planed and the real world issues you have to consider. Also what is new with PHP 5.3 on FPM and how you can benefit from it.

How does it work ?^

In short: the apache server delegates incoming requests to (external) persistent PHP processes and retrieves/returns the output back to the client. The external running PHP processes are controlled by a process manager (php-pm or php-fpm), which can fork child processes. All childs withunder the same master process are able to share resources, eg an opcode cache as APC. This is quite different from how mod_php (the PHP runtime is started within the apache process) and normal CGI works (a new process is started for each request). In contrast to mod_php your processes can run under different (Unix/OS) users via suexec – while still being persistent – which reduces the danger of being compromised by a magnitude.

Most FastCGI implementations also provide (another) process manager, which tries itself to handle the child processes and can interfere with the one from PHP itself.

What FastCGI implementations are there ?^

First of, there is mod_fastcgi which is kind of the mother of all FastCGI implementations in apache. Under normal circumstances it works like a charm. However, it has serious problems if you run into certain scenarios in which PHP processes run longer then your idle-timeout expects. Then there is the newer mod_fcgid, which is a more recent implementation of FastCGI and provides better methods to achieve stability and control. The downside is, that it cannot use any externally started FastCGI servers. Aside from those “majors”, you can use mod_fastcgi_handler, a scaled down (no real process manager, cause PHP has it’s own) and slim re-implementation of the problem. Unfortunately it is quite experimental/beta. From Apache 2.3+ you can also try mod_proxy_fcgi, based on mod_proxy, also without any process managing capabilities.

How to use ..^

The goal of each implementation will always be to have a file-suffix-bound PHP support, meaning: all files with .php are executed by the FastCGI process. I do not care about any directory-based (eg /fcgi-bin) approaches. Any following examples should work with PHP 5.2′s and PHP 5.3′s php-cgi – but not with PHP FPM, which i will cover later on.


The upside is, it is very stable and quite capable of tracking down (and killing) dead or dysfunctional processes. On the downside, you do not want to use it, if you need any opcode cache (such as APC or XCache). It delegates any new request to another instance of the PHP process manager – not to it’s childs. So if you want to server more then one request at a time, you need to start multiple master processes, which might have childs, but the only share the cache between those. If you do not need any opcode cache, you are fine (then again: if so, you probably do not need a persistent/performant PHP, due to lack of requests). Also it will NOT work anyhow with PHP 5.3 on FPM! Do not bother to try. It cannot, cause it does not implement any method to communicate via socket or ip/port with external started processes.

You need a starter script for your application, because we’re using suexec (run the php process under another user then www-data) and it’s security settings require to have the executable scripts withunder /var/www (as long as you do not re-compile it yourself).

Wrapper /var/www/wrapper/fcgid


exec /usr/bin/php-cgi

Assure the script is executable and itself as well as the above directory belongs to the user and group you specify with SuexecUserGroup in the VirtualHost. Following a minimal VirtualHost:

<VirtualHost *:80>
 FCGIWrapper /var/www/wrapper/fcgid .php
 AddHandler fcgid-script .php
 SuexecUserGroup uk uk
 ServerName some-host
 DocumentRoot /var/www/sites/some-site

Thats all. You can now configure mod_fcgid not to start more then X processes and/or to keep always Y processes available and so on. However, it’s no fun as long as those processes do not fork and thus do not share resources. Thats why i will not go further into it. Use mod_fcgid for Perl, Ruby, Python or whatever you prefer .. but not PHP.


Using this module, you probably want to disable most controlling features of it’s process manager and let’s PHP handle the rest. Depending on the amount of processes on you whole webserver, you have to adjust the max amount of runnable processes somewhere global, eg /etc/apache2/http.conf:

FastCgiConfig -killInterval 60 -maxClassProcesses 1 -maxProcesses 50 -minProcesses 0 -startDelay 5

The important parts here are maxClassProcesses (from the mod_fastcgi point of view, each VirtualHost with FastCGI will use only one (master) process) and maxProcesses, which determines the total max amount of processes across your whole server (think: 50 VHosts with each one master).

Now tell the mod_fastcgi module (debian: /etc/apache2/mods-enabled/fastcgi.conf) to use the suexec wrapper:

<IfModule mod_fastcgi.c>
 AddHandler fastcgi-script .fcgi
 FastCgiWrapper /usr/lib/apache2/suexec
 FastCgiIpcDir /var/lib/apache2/fastcgi

You also need a wrapper script. For the same reasons as above but also for determining the amount of forked children. Is assume it is in /var/www/wrapper/fastcgi:


# User uk has it's ini file in /var/www/ini/uk/php.ini
#export PHPRC="/var/www/ini/uk"

# so much forked children

exec /usr/bin/php5-cgi

Then you can setup the VirtualHost:

FastCgiServer /var/www/wrapper/fastcgi -processes 1 -user uk -group uk -idle-timeout 310 -flush

<VirtualHost *:80>
 ScriptAlias /php-fastcgi-uk /var/www/wrapper/fastcgi
 Action php-fastcgi-uk /php-fastcgi-uk
 AddHandler php-fastcgi-uk .php
 SuexecUserGroup uk uk
 ServerName some-host
 DocumentRoot /var/www/sites/some-site

What we do here is starting the FastCgiServer (from apache) via the wrapper command as user uk and group uk and tell the FastCGI PM that there shall be one process. The -flush is optional, it assures that requests will be passed unbuffered to the FastCGI servers – i’ve experienced strange hanging processes without. The most important part is the -idle-timeout which i will go into later on.


Here you do not need any wrapper script, but a starter script, because mod_fastcgi_handler can only deal with external started processes. Here you go:


# User uk has it's ini file in /var/www/ini/uk/php.ini
#export PHPRC="/var/www/ini/uk"

# so much forked children

exec /usr/bin/php5-cgi -b

Then in your VirtualHost:

<VirtualHost *:80>
 <Files *.php>
  SetHandler fcgi:
 SuexecUserGroup uk uk
 ServerName some-host
 DocumentRoot /var/www/sites/some-site

After (re)starting apache, you have to start the external script manually, eg via start-stop-daemon.


I did not tried it out, due to not having a running Apache 2.3. In any case, i think it should be fairly simple – if you can build your apache yourself, you will have no problems setting it up. For now it is not a valid choice, because 2.3 is far from being stable and thus deployed in most distros.


Since PHP 5.3.3 FPM is part of PHP and, well, it is a big improvement, because it is the first full grown and (so far i experienced) stable PHP PM ever. It has to be started externally which excludes mod_fcgid from the start and leaves us with mod_fastcgi* and – as soon as 2.3 is stable – mod_proxy_fcgi. But it also brings new features to the table: rlimit for max open files, enforcing the max_execution_time from the PHP process and a dynamic process management, with which you can reduce the amount of idle processes and by this the amount of wasted memory.

Before i go into the actual implementation, i want to share a significant problem with PHP and FastCGI which you might run into and which cost myself hours and hours of research until i figured out what the problem was in the first place.

The timeout problem^

With php-cgi and mod_fastcgi (or mod_fcgid) you do have two separate timeouts for your processes: one of the FastCGI PM (idle-timeout in mod_fastcgi) and one from the PHP processes (max_execution_time). As long as the latter is below the first – everything works fine. But you(r users) can modify the max_execution_time at runtime (ok, ini_set could be disabled, but it would break most installable applications out there), which then let the the idle-timeout from the FastCGI PM do it’s terrible work. Not even now i am quite sure what exactly happens, but let me show how it looks like:

  • In your apache error log, you’ll find a lot of “FastCGI: incomplete headers (0 bytes) received from server”
  • The processes are not restarted by mod_fastcgi and you have to kill them manually
  • Sometimes: even after full apache restart, the processes go directly into their zombie-idle state and do not respond
  • A trace of the process will show you something like this, over and over again:
    clock_gettime(CLOCK_MONOTONIC, {267231, 352083330}) = 0
    clock_gettime(CLOCK_MONOTONIC, {267231, 352083330}) = 0
    clock_gettime(CLOCK_MONOTONIC, {267231, 352083330}) = 0
    poll([{fd=5, events=POLLIN|POLLPRI}], 1, 1000) = 0 (Timeout)
    poll([{fd=5, events=POLLIN|POLLPRI}], 1, 0) = 0 (Timeout)

The nasty part is, that it does not only affect one forked child, but all. It kind of looks like, as if the FastCGI PM just cuts the IO stream to the PHP processes and leaves it there, wondering where everybody has gone.

You could adjust the idle-timeout of mod_fastcgi to a very high value and this problem will not occur – but if it’s really required to restart the processes from the PM, you’ll have a problem. This is where FPM comes in. It provides a config option called request_terminate_timeout, which enforces termination of the PHP processes for sure. What you can do now, is set the max_execution_time to a reasonable value, then adjust  request_terminate_timeout to something longer, which cannot be overwritten by the user and then set the FastCGI idle-timeout right above.


I will show how to use it with mod_fastcgi, cause mod_fastcgi_handler complained errors in the FastCGI answer from FPM, mod_proxy_fcgi is still part of Apache 2.3 and mod_fcgid will not work. It is a bit trickier to implement then it was with PHP 5.2.x and php-cgi but once you understood the general idea, it is not that hard.

Let’s begin with the FPM itself. FPM needs a configuration file (one per user) and optional a path to a directory containing a customized php.ini. Here is the fpm.conf:

pid = /tmp/
error_log = /var/log/apache2/fpm-uk.log
log_level = error
listen = /tmp/fpm-uk.sock
listen.owner = uk = www-data
listen.mode  = 0660
user = uk
group = uk
pm = dynamic
pm.max_children = 10
pm.start_servers = 2
pm.min_spare_servers = 3
pm.max_spare_servers = 10
pm.max_requests = 0
request_terminate_timeout = 305
slowlog = /var/log/apache2/fpm-slow-uk.log

The most important parts are the listen and user settings:

  • listen
    Can be either a path to a socket or an ip:port.
  • listen.owner,, listen.mode
    Ownership of the socket file. Either set the owner or the group to your apache user/group (debian: www-data). With mode assure that apache can access the socket read/write.
  • user, group
    The actual user/group under which the PHP processes wil run.
  • pm, pm_max_children,pm_start, pm…
    The PM is set to dynamic, this means it will start start_server from the beginning, keep at minimum 3 spare servers and goes up to max 10 servers at a time.
  • request_terminate_timeout
    This will overrule the max_execution_time and will kill any PHP process before the FastCGI idle-timeout jumps in.

Read the documentation file configuration guide for more detailed explanation.

Now it’s getting tricky, because for apache we need a quite strange setup. Let me first show you how it will look like and then explain how it works.

FastCgiExternalServer /var/www/sites/virtual-some-site -socket /tmp/fpm-uk.sock -user uk -group uk -idle-timeout 310 -flush

<VirtualHost *:80>
 <Files *.php>
  Options +ExecCGI
 AddHandler php5-fpm-uk .php
 Action php5-fpm-uk /virtual-some-site
 Alias /virtual-some-site /var/www/sites/virtual-some-site/www
 SuexecUserGroup uk uk
 ServerName some-host
 DocumentRoot /var/www/sites/some-site

The trick is to setup a second directory, in which a soft link to the actual DocumentRoot exists. The first argument of the FastCgiExternalServer directive says: execute everything under this directory as PHP. We can not use the actual document root (/var/www/sites/some-site) directly, because this would lead to piping of everything (.jpg, .html, ..) through the handler. With the Alias directive we connect the soft linked directory under virtual-some-site to the actual directory. Action declares then a new action with the name php5-fpm-uk with the aliased directory and AddHandler links all .php-files with the action. The Files block just assures all .php-files are executable for CGI. There some other working configurations, but this one will do the job, especially with heavy usage of RewriteRule-directives, which will break many other FPM configurations.

Here is how you setup the virtual directory:

mkdir -p /var/www/sites/virtual-some-site
cd /var/www/sites/virtual-some-site
ln -s ../some-site www
chown -h uk:uk www

If you run into problems, assure that you have either globally activated SymLinksIfOwnerMatch or on the directory:

<Direoctory /var/www/sites/virtual-some-site>
 Options +SymLinksIfOwnerMatch

Having this done you can restart apache and start the FPM server:

php5-fpm -c /var/www/config/uk/ini -y /var/www/config/uk/fpm.conf

You are not yet done, cause so far it might break you application because the environment ($_SERVER) variables SCRIPT_NAME, SCRIPT_FILENAME and PHP_SELF contain the virtual path as well. So you have to remove them at runtime. This can be automated by a auto-prepended PHP file via the php.ini:

# add to you php.ini
auto_prepend_file = /var/www/config/uk/prepend.php

And then in the prepend file (/var/www/config/uk/prepend.php):

$_SERVER[ 'SCRIPT_NAME' ] = str_replace( '/virtual-some-site/', '/', $_SERVER[ 'SCRIPT_NAME' ] );
$_SERVER[ 'SCRIPT_FILENAME' ] = str_replace( '/virtual-some-site/', '/', $_SERVER[ 'SCRIPT_FILENAME' ] );
$_SERVER[ 'PHP_SELF' ] = str_replace( '/virtual-some-site/', '/', $_SERVER[ 'PHP_SELF' ] );

This might not be as clean as i would like it, but it does work. I played around with a lot of configurations and this was the only one working under all circumstances (as mentioned above, with a lot of RewriteRules you might end up with infinite loops). One thing to mention: make sure the name of you virtual directory is unique (not just “virtual” or “php”) so you can replace it easily without breaking your app..


PHP on FastCGI has it’s hiccups and problems, but as long as you know them you can work around. In any case, it is much safer then mod_php in a shared environment. Howerver, if you still are a fan of mod_php or you think the effort is not worth the the result, you might want to have a look at Apache MPM ITK. This MPM allows you to assign a user/group for mod_php. The downside is still that you cannot control the amount of used resources (as in: 5 PHP processes max) and it is a fork of MPM prefork (half as fast as MPM worker for static files).

As always: no warranties or guaranties of any kind are given. Make sure you test all this in a save development environment.