Update, March 2015 – This walkthrough is one of the most complete on the subject – please go to the comments section with any questions or comments you have. I’m happy to respond to any issues you are having or respond to any outdated instructions due to new software versions.

A guide written through much grief, and possibly sorrow and pain.

Configuration example given to overide a single virtualhost to disable suPHP and prefer FastCGI.

Apache 2.2 running PHP 5.3 under mod_fastcgi through suEXEC on CentOS 5.8 on WHM/Cpanel Installations

Configuring a WHM/Cpanel based site with a significant amount of traffic to use fastcgi to take its performance to the next level can seem like a great idea. In fact, in order to make use of APC caching, it might be your only choice if you want to run scripts as their owner/group, unlike DSO/mod_php, which while fast, can be less secure. SuPHP, a commonly used PHP handler, is unable to work with Op-code (compiled PHP in this case) caching systems like APC. SuPHP tears down its process (and starts up again) after every request, taking a performance impact that also prevents such caching systems from persisting to share resources.

In this writeup, I’ll cover some of the issues I encountered while configuring a CentOS 5.8 server to use PHP under fastcgi with Apache’s mod_fastcgi module. One of my primary goals was to allow a Cpanel/WHM setup to continue to work unaltered– to maintain script ownership and permissions and avoid rebuilding PHP or Apache. You may find yourself wanting to improve performance over SuPHP without venturing too far away from the stable server configuration and software stack in place for your production server.

I had to scour countless resources in order to compile together a configuration that worked for me, despite many resources preferring mod_fastcgi over mod_fcgi and other configurations. You may find many writeups that advocate using NGINX, PHP-FPM, or apache running under MPM-worker or MPM-event mode. I will not cover these here; there is a plethora of writeups to cover those use cases. A quick recap: mod_fastcgi is advocated more widely than mod_fcgi, but Cpanel/WHM installations offer only the latter as an easily configurable choice. With that said, let’s get started!

Step 1: Installing and Enabling mod_fastcgi

As I mentioned before, WHM/Cpanel makes it (somewhat) easy to enable mod_fcgi (through EasyApache). We want to install mod_fastcgi instead. Therefore, we will install it from source.

Downloading and Installing mod_fastcgi

Before I could proceed, I had to edit the package exclusion list in place by Cpanel to maintain system packages with its own versions: /etc/yum.conf

Remove the portion of the line near the top that contains “httpd*” — ensure the surrounding words on the line are separated with a space as they previously were. After we are done we will put this exclusion back.

Install the httpd-devel package using yum as root: yum install httpd-devel

Download the latest release of the mod_fastcgi module:

cd /opt && wget http://www.fastcgi.com/dist/mod_fastcgi-current.tar.gz

Extract the archive and change into the expanded directory:

tar -zxvf mod_fastcgi-current.tar.gz && cd mod_fastcgi-*

The above command should work with any version of the package.

Make a renamed copy of the Makefile.

cp Makefile.AP2 Makefile

Make and install the module– I will choose to install only in the Cpanel directory. You may instead select your system architecture’s lib folder and create a symbolic link in the Cpanel apache installation directory.

make top_dir=/usr/local/apache/modules
make install top_dir=/usr/local/apache/modules

If you get any errors, you may have missing system libraries that you’ll need to fetch with yum.

If you have gotten this far without errors, go ahead and edit /etc/yum.conf, adding “httpd*” back to the exclusion list.

Configure Apache to load mod_fastcgi

To configure apache to load mod_fastcgi, we’ll want to modify the apache configuration file. Cpanel modifies this file and suggests all edits be made in other files which are then included in the configuration. In my case, I chose “/usr/local/apache/conf/includes/pre_virtualhost_2.conf” rather than a file in the conf.d subfolder (these do not appear to be autoloaded by Cpanel’s apache installation). I could have selected a location like “/usr/local/apache/conf/includes/pre_main_2.conf“, but instead I settled for “/usr/local/apache/conf/includes/pre_virtualhost_2.conf“, where I will also make other edits.

Edit “/usr/local/apache/conf/includes/pre_virtualhost_2.conf” to include the following line:

LoadModule fastcgi_module modules/mod_fastcgi.so

Save the file, reload apache, and your error logs should report the module as loaded. To give it a try, you can do the following:

/etc/init.d/httpd restart && httpd -M | grep "fastcgi_module"

It will show a line with the match if the module is loaded.

This should have you up and running with mod_fastcgi without any recompilation using EasyApache.

Step 2: Configuring mod_fastfcgi to launch PHP files under suEXEC

Now that you have mod_fastcgi enabled, we can now proceed to configuring it.

Configuring mod_fastcgi to use suEXEC

As I alluded to so recently, you’ll be editing “/usr/local/apache/conf/includes/pre_virtualhost_2.conf” again.

Add the following lines to your copy of the file:
FastCgiWrapper /usr/local/apache/bin/suexec

This will direct apache to wrap requests for fastcgi execution through apache’s suEXEC utility. Be careful– you’ll get errors if the user/group permissions on any fastcgi scripts do not match the user/group they are launched under.

Luckily, if you are migrating away from a suPHP configuration and using Cpanel/WHM, your configuration likely has user/groups defined for each virtualhost that will match the system user for that folder of the computer (/home/[USER]/public_html/…).

You’ll also have aliases for each virtualhost that set the path /cgi-bin/ in apache to map to /home/[USER]/public_html/cgi-bin, which your user will have read/write access to. We will look at this next.

Setting up a PHP Wrapper Script

In order to launch PHP through FastCGI, we will create a script which controls PHP through some basic environmental variables. Create a file in /home/[USER]/public_html/cgi-bin/. I will call mine “php5.fcgi“.

Make your copy of the file with the following contents:

#!/bin/sh
PHP_FCGI_CHILDREN=4
export PHP_FCGI_CHILDREN
PHP_FCGI_MAX_REQUESTS=5000
export PHP_FCGI_MAX_REQUESTS
exec /usr/bin/php -c /usr/lib/fcgi-php.ini

You may want to tune the numbers for either parameter. On the last line, you may omit the portion of the line following exec /usr/bin/php — since I am configuring one virtualhost to use PHP through FastCGI and the rest to continue using suPHP, I have a separate PHP configuration file specified for all requests that go through the wrapper script over FastCGI. This is particularly helpful since I wanted to enable APC caching.

Now that you have created the file, ensure that it is executable by issuing the following command: chmod +x /home/[USER]/public_html/cgi-bin/php5.fcgi — be sure to replace [USER] with the correct username, of course. Next, we’ll be setting up apache to use the wrapper script to handle PHP requests.

Override the PHP Handler for a Single Virtualhost

Before you can continue, you’ll want to uncomment the relevant line in “/usr/local/apache/conf/httpd.conf” that includes an extra configuration file for your virtualhost. It should look something like this:

# To customize this VirtualHost use an include file at the following location
# Include "/usr/local/apache/conf/userdata/std/2/[USER]/[VHOST]/*.conf"

Remove the pound sign (#) before the word “Include” and then we can create a file that matches that location. Again, be sure to replace references to [USER] and [VHOST] with the actual values.

mkdir -p /usr/local/apache/conf/userdata/std/2/[USER]/[VHOST]/

This command will create the file structure needed for the configuration file, creating any missing subdirectories that don’t yet exist. Now you can successfully edit a file at that location. Here, I selected “fast_cgi.conf“, but you can use anything you’d like. Be sure that it is in the deepest folder you just created.

Edit your file to include the following, making changes/substitutions as necessary.

FastCgiServer /home/[USER]/public_html/cgi-bin/php5.fcgi -processes 1 -user [USER] -group [GROUP] -idle-timeout 310 -flush -pass-header AUTHORIZATION

suPHP_Engine off
AddType application/x-httpd-php5-fcgi .php
Action application/x-httpd-php5-fcgi /cgi-bin/php5.fcgi
<Directory /cgi-bin/>
        SetHandler fastcgi-script
        Options +ExecCGI
</Directory>
<Location "/cgi-bin/php5.fcgi">
Order Deny,Allow
Deny from All
Allow from env=REDIRECT_STATUS
</Location>

The last half of the configuration could probably be combined into one section, but I have not bothered to consolidate them. This configuration sets suPHP off for the given virtualhost, executes a persistent php process with our wrapper script using suEXEC, and sets it as our handler for PHP files. Near the bottom of the file we ensure that the wrapper script cannot be accessed directly.

Wrapping up & Putting it All Together

Restart apache (/etc/init.d/httpd restart) and check for any error messages it displays. Also be sure to check out your error log (/usr/local/apache/logs/error_log) for any notices that might give away any problems. With any luck, you’ll be all set!

As with any server environment changes, be sure to be careful when working on a production server environment. If you have any Cpanel/WHM installations on other servers you can test freely on, I highly recommend it. Remember that no configuration will always be ideal for everyone, and you may find deviating from the settings I specified may yield better performance for you. I hope that this guide serves as a good starting point for Cpanel/WHM users who want to get extra performance. Fast CGI on its own can help greatly, and being able to maintain security through permissions is a great plus. I highly recommend exploring APC caching if you are trying to squeeze more performance out of your installation.

This should help you to get started without deviating from Cpanel’s base configuration too greatly, to help blend the benefits of suPHP with the kind of performance persistent PHP processes can offer.

I leave you with a few final commands to check out while trying to debug your installation:

ps aux | grep "php" should yield multiple lines ideally. pstree -up with the first pid should show the process tree indicating child php processes forked from the main wrapper script’s PHP execution.