This setup is typically used in shared web hosting environments. Why this method rather than using mod_php, well quite simply, it allows php, perl or even python code to be executed as the user which owns it which means that the user will not be able to access user space that their user does not have permission to. It also offer the ability to do true process accounting and track which site is using the most resources on a host. It also offers a php.ini file for each site hosted, as well as on the fly changes to said file because php calls it as scripts are executed. This type of setup would not normally be used on a server hosting a single web page although there could be advantages to doing so, but I will not go into these. Note: This guide also assumes that you know how to setup virtual hosts the normal way using mod_php.
Installation
The following packages will need to be installed. (These are what they are called on Debian, RedHat etc might be different)
- apache2
- libapache2-mod-fastcgi
- apache2-suexec
Virtual Hosts
Create the required directory structure and then set up your virtual hosts as normal
<VirtualHost *:80> ServerName linux-101.org DocumentRoot /var/www/linux-101/webroot ServerAdmin hostmaster@linux-101.org ErrorLog /var/log/apache2/linux-101.org_error.log CustomLog /var/log/apache2/linux-101.org_access.log combined <Directory "/var/www/linux-101/webroot"> Options Indexes Includes FollowSymLinks AllowOverride All Order allow,deny Allow from all </Directory> </VirtualHost>
Restart apache and test.
All working (without PHP, because we're not using mod_php and fastCGI is not ready yet)? Good.
FastCGI
Now assuming that your web directory is /var/www/linux-101/webroot create a new directory /var/www/linux-101/.cgi-bin and place the following script inside:
php5.fcgi
#!/bin/bash # # php5.fcgi # Shell Script to run PHP5 using mod_fastcgi under Apache 2.x # #USER=$(/usr/bin/whoami) #PHPRC="/var/www/$USER/.cgi-bin/php.ini" PHP_FCGI_CHILDREN=5 #PHP_FCGI_MAX_REQUESTS=1000 #export PHPRC export PHP_FCGI_CHILDREN #export PHP_FCGI_MAX_REQUESTS exec /usr/bin/php5-cgi
Make this shell script executable
# chmod 755 php5.fcgi
Now, modify your virtual host to look like this:
Note: Apache actions module needs to be enabled, and mod_php disabled.
<VirtualHost *:80> ServerName linux-101.org DocumentRoot /var/www/linux-101/webroot ServerAdmin hostmaster@linux-101.org ErrorLog /var/log/apache2/linux-101.org_error.log CustomLog /var/log/apache2/linux-101.org_access.log combined ScriptAlias /cgi-bin/ "/var/www/linux-101/.cgi-bin/" <Directory /var/www/linux-101/.cgi-bin/> AllowOverride None Options None Order allow,deny Allow from all </Directory> <Directory "/var/www/linux-101/webroot"> Options Indexes Includes FollowSymLinks ExecCGI AllowOverride All AddHandler php5-fastcgi .php .php5 .php4 Action php5-fastcgi /cgi-bin/php5.fcgi Order allow,deny Allow from All </Directory> </VirtualHost>
Note the line ScriptAlias /cgi-bin/ "/var/www/linux-101/.cgi-bin/", the cgi-bin directory directive and the extra options to the webroot directive.
Create the file /var/www/linux-101/webroot/phptest.php
<?PHP phpinfo(); ?>
Now browse to it and you should see a long page with all the PHP environment variables.
suEXEC
Till now, all your php scripts have been running as the www-data (Debian) user, which means that the script will have access to anything that the apache user has access to. On a shared server, it means that any user is able to access (be it read or write) data in another user's web directory because apache has access. This is where suEXEC comes in. It forces our php apps to run as the user who owns them. Note: suEXEC has been compiled with the default docroot, which is normally /var/www and therefore will not work for files outside of this path.
So, create a new user for your first virtual host. (Mine is called linux-101)
# useradd -d /var/www/linux-101 linux-101
Now copy /etc/php5/cgi/php.ini to /var/www/linux-101/.cgi-bin/
# cp -fv /etc/php5/cgi/php.ini /var/www/linux-101/.cgi-bin/
Now that you have a site specific php.ini file, edit it and set open_basedir
; open_basedir, if set, limits all file operations to the defined directory open_basedir = /var/www/linux-101
Next, modify php5.fcgi to look like this:
#!/bin/bash # # php5.fcgi # Shell Script to run PHP5 using mod_fastcgi under Apache 2.x # USER=$(/usr/bin/whoami) PHPRC="/var/www/$USER/.cgi-bin/php.ini" PHP_FCGI_CHILDREN=5 #PHP_FCGI_MAX_REQUESTS=1000 export PHPRC export PHP_FCGI_CHILDREN #export PHP_FCGI_MAX_REQUESTS exec /usr/bin/php5-cgi
We now need to make sure that linux-101 owns the dir and all the files below it.
# chown -Rf linux-101:linux-101 /var/www/linux-101 # chown -Rf linux-101:linux-101 /var/www/linux-101/.cgi-bin
And if you don't want your users changing php.ini to suite their own needs
# chown root:root /var/www/linux-101/.cgi-bin/php.ini
Set permissions on the parent dir so no one else can get in.
# chmod 710 /var/www/linux-101
And then make sure apache still is able to open the parent directory.
# chown linux-101:www-data /var/www/linux-101
Lastly, add the following important line to your virtual host configuration:
SuexecUserGroup linux-101 linux-101
This tells apache which user and group to use when executing files/apps.
Your final virtual host config should look something like this:
<VirtualHost *:80> ServerName linux-101.org DocumentRoot /var/www/linux-101/webroot ServerAdmin hostmaster@linux-101.org ErrorLog /var/log/apache2/linux-101.org_error.log CustomLog /var/log/apache2/linux-101.org_access.log combined SuexecUserGroup linux-101 linux-101 ScriptAlias /cgi-bin/ "/var/www/linux-101/.cgi-bin/" <Directory /var/www/linux-101/.cgi-bin/> AllowOverride None Options None Order allow,deny Allow from all </Directory> <Directory "/var/www/linux-101/webroot"> Options Indexes Includes FollowSymLinks ExecCGI AllowOverride All AddHandler php5-fastcgi .php .php5 .php4 Action php5-fastcgi /cgi-bin/php5.fcgi Order allow,deny Allow from All </Directory> </VirtualHost>
Now, when you browse to phptest.php, the php processing should be done by the user, and not as www-data or whatever your apache user is called.
To test, run
# ab -c 100 -n 10000 http://example.com/
against your site. This will generate 10,000 (100 simultaneously) requests to your site and by running top you should see your top process be php5-cgi owned by the user who owns that site.
Repeat these steps for each virtual host, substituting linux-101 for your own sites.

