Running PHP scripts using mod_fcgid

Why use mod_fcgid for PHP scripts?

According to the official Apache mod_fcgid documentationmod_fcgid is a high performance alternative to mod_cgi or mod_cgid, which starts a sufficient number [of] instances of the CGI program to handle concurrent requests, and these programs remain running to handle further incoming requests. It is favoured by the PHP developers, for example, as a preferred alternative to running mod_php in-process, delivering very similar performance.

Strangely however the description omits the two main reasons why one would wish to use an alternative to running mod_php in-process.

The first, and probably the most important of these reasons, is that a mod_php enabled Apache process consumes a significant quantity of memory. In our tests a mod_php enabled Apache process consumed between 35MB and 85MB of memory each! Running mod_fcgid instead dropped the per-process memory usage to roughly 4MB.

The second significant reason is a result of the first - with higher per-process memory usage fewer processes can be started to serve requests thus increasing the average time to serve a request on busy sites. This becomes even more significant when you consider that the average page requires only a single PHP request but may generate dozens of requests for static content (such as graphics, stylesheets, etc).

Installing mod_fcgid

Installing the www-apache/mod_fcgid package is trivial, as usual with Gentoo Linux . The example below demonstrates how this may be accomplished. As you can see there are no use-flags to customise.

lisa emerge www-apache/mod_fcgid -pv
These are the packages that would be merged, in order:

Calculating dependencies... done!
[ebuild      ] www-apache/mod_fcgid-2.3.7
lisa emerge www-apache/mod_fcgid

Once the www-apache/mod_fcgid package has been installed you will need to replace the -D PHP5 entry in the Apache2 daemon configuration file with -D FCGID, as shown below.

/etc/conf.d/apache2
APACHE2_OPTS="-D DEFAULT_VHOST -D PHP5"
APACHE2_OPTS="-D DEFAULT_VHOST -D FCGID"
Caution:
If there are other entries besides the -D DEFAULT_VHOST and -D PHP5 options in the APACHE2_OPTS variable these should be preserved or some functionality will be lost.
 

Configuring apache to use mod_fcgid

The www-apache/mod_fcgid package installs a default global configuration file at /etc/apache2/modules.d/20_mod_fcgid.conf which should be modified before the server is restarted. The example below provides both a description as well as a suitable starting point for all the significant settings.

/etc/apache2/modules.d/20_mod_fcgid.conf
<IfDefine FCGID>
LoadModule fcgid_module modules/mod_fcgid.so

SocketPath /var/run/fcgidsock
SharememPath /var/run/fcgid_shm

# Define handler for .php files.
AddHandler fcgid-script .php
FcgidWrapper /usr/bin/php-cgi .php

# Define the MIME-Type for .php files.
AddType application/x-httpd-php .php

# The setting of FcgidFixPathinfo should mirror the cgi.fix_pathinfo setting in php.ini.
FcgidFixPathinfo 1

# Configure process limits and spawn rate.
FcgidMaxProcesses 5
FcgidMaxProcessesPerClass 5
FcgidMinProcessesPerClass 5
FcgidSpawnScoreUpLimit 100

# Allow large (20MB) requests (such as uploading to WordPress).
MaxRequestLen 20971520

# Increase timeouts.
IPCCommTimeout 60
FcgidBusyTimeout 60
FcgidConnectTimeout 20

# Path to php.ini.
FcgidInitialEnv PHPRC=/etc/php/cgi-php5.4

# Number of PHP child processes that will be launched (should be zero).
FcgidInitialEnv PHP_FCGI_CHILDREN 0

# Maximum requests before a process is stopped and a new one is launched.
FcgidMaxRequestsPerProcess 1000
FcgidInitialEnv PHP_FCGI_MAX_REQUESTS 1000
</IfDefine>
Caution:
In our experience a php-cgi instance usually uses between 30MB and 40MB of memory each. The above example starts a maximum of five processes which will therefore use between 150MB and 200MB. Care should be taken when selecting a value for FcgidMaxProcesses to ensure that sufficient RAM is available.
 

Configuring virtual hosts to use mod_fcgid

Once the global configuration has been modified to your liking the last remainaing task is to configure the virtual servers which require PHP support. The example below demonstrates how the default configuration for the default virtual host would need to be modified.

/etc/apache2/vhosts.d/default_vhost.include
# Ensure that index.php is used as an index page.
DirectoryIndex index.php index.html

# Stop the Are-You-Dead-Yet attack.
RequestReadTimeout header=20-40,MinRate=500 body=20,MinRate=500

# Set the document root.
DocumentRoot "/var/www/localhost/htdocs"

# Options and permissions for document root and below.
<Directory "/var/www/localhost/htdocs">
# Allow execution of CGI scripts.
Options +ExecCGI

AllowOverride All

Order allow,deny
Allow from all
</Directory>
Caution:
The RequestReadTimeout entry above is important as without it an attacker can easily overwhelm the available php-cgi instances effectively creating a Denial of Service.
 

Once the required changes have been made to all the virtual servers requiring PHP support the server will need be restarted for the changes to become effective, as shown below.

lisa /etc/init.d/apache2 restart