Set Up Apache2 With mod_fcgid And PHP5 On Ubuntu 9.04


This tutorial describes how you can install Apache2 with mod_fcgid and PHP5 on Ubuntu 9.04. mod_fcgid is a compatible alternative to the older mod_fastcgi. It lets you execute PHP scripts with the permissions of their owners instead of the Apache user.
I do not issue any guarantee that this will work for you!
 
1 Preliminary Note
I'm using an Ubuntu 9.04 server in this tutorial with the hostname server1.example.com and the IP address192.168.0.100.
I will create two Apache vhosts in this tutorial, www.example1.com and www.example2.com, to demonstrate the usage of mod_fcgid.
Make sure you're logged in as root:
sudo su
/bin/sh is a symlink to /bin/dash, however we need /bin/bash, not /bin/dash. Therefore we do this:
dpkg-reconfigure dash
Install dash as/bin/sh? <-- No
In addition to that, we must disable AppArmor:
/etc/init.d/apparmor stopupdate-rc.d -f apparmor removeaptitude remove apparmor apparmor-utils
 
2 Installing Apache2/mod_fcgi/PHP5
In order to install Apache2, mod_fcgid, and PHP5, run
aptitude install apache2 apache2-suexec libapache2-mod-fcgid php5-cgi
If Apache2 was already installed with PHP5 as an Apache module, disable the PHP5 module now:
a2dismod php5
Then enable the following modules...
a2enmod rewritea2enmod suexeca2enmod includea2enmod fcgid
... and open /etc/php5/cgi/php.ini:
vi/etc/php5/cgi/php.ini
Add the line cgi.fix_pathinfo = 1 right at the end of the file:
[...]
cgi.fix_pathinfo = 1

Then restart Apache:
/etc/init.d/apache2 restart
 
3 Creating Vhosts For www.example1.com And www.example2.com
I will now create two vhosts, www.example1.com (with the document root /var/www/web1/web) and www.example2.com(with the document root /var/www/web2/web). www.example1.com will be owned by the user and group web1, andwww.example2.com by the user and group web2.
First we create the users and groups:
groupadd web1groupadd web2useradd -s/bin/false -d/var/www/web1 -m -g web1 web1useradd -s/bin/false -d/var/www/web2 -m -g web2 web2
Then we create the document roots and make them owned by the users/groups web1 resp. web2:
mkdir -p/var/www/web1/webchown web1:web1/var/www/web1/webmkdir -p/var/www/web2/webchown web2:web2/var/www/web2/web
We will run PHP using suExec; suExec's document root is /var/www, as the following command shows:
/usr/lib/apache2/suexec -V
root@server1:~# /usr/lib/apache2/suexec -V -D AP_DOC_ROOT="/var/www" -D AP_GID_MIN=100 -D AP_HTTPD_USER="www-data" -D AP_LOG_EXEC="/var/log/apache2/suexec.log" -D AP_SAFE_PATH="/usr/local/bin:/usr/bin:/bin" -D AP_UID_MIN=100 -D AP_USERDIR_SUFFIX="public_html"root@server1:~#
Therefore we cannot call the PHP binary (/usr/lib/cgi-bin/php) directly because it is located outside suExec's document root. As suExec does not allow symlinks, the only way to solve the problem is to create a wrapper script for each web site in a subdirectory of /var/www; the wrapper script will then call the PHP binary /usr/lib/cgi-bin/php. The wrapper script must be owned by the user and group of each web site, therefore we need one wrapper script for each web site. I'm going to create the wrapper scripts in subdirectories of /var/www/php-fcgi-scripts, e.g. /var/www/php-fcgi-scripts/web1 and /var/www/php-fcgi-scripts/web2.
mkdir -p/var/www/php-fcgi-scripts/web1mkdir -p/var/www/php-fcgi-scripts/web2
vi/var/www/php-fcgi-scripts/web1/php-fcgi-starter
#!/bin/sh
PHPRC=/etc/php5/cgi/
export PHPRC
export PHP_FCGI_MAX_REQUESTS=5000
export PHP_FCGI_CHILDREN=8
exec /usr/lib/cgi-bin/php

vi/var/www/php-fcgi-scripts/web2/php-fcgi-starter
#!/bin/sh
PHPRC=/etc/php5/cgi/
export PHPRC
export PHP_FCGI_MAX_REQUESTS=5000
export PHP_FCGI_CHILDREN=8
exec /usr/lib/cgi-bin/php

The PHPRC line contains the directory where the php.ini file is located (i.e., /etc/php5/cgi/ translates to/etc/php5/cgi/php.ini). PHP_FCGI_MAX_REQUESTS is the maximum number of requests before an fcgid process is stopped and a new one is launched. PHP_FCGI_CHILDREN defines the number of PHP children that will be launched.
The php-fcgi-starter scripts must be executable, and they (and the directories they are in) must be owned by the web site's user and group:
chmod 755/var/www/php-fcgi-scripts/web1/php-fcgi-starterchmod 755/var/www/php-fcgi-scripts/web2/php-fcgi-starterchown -R web1:web1/var/www/php-fcgi-scripts/web1chown -R web2:web2/var/www/php-fcgi-scripts/web2
Now we create the Apache vhosts for www.example1.com and www.example2.com:
vi/etc/apache2/sites-available/web1
<VirtualHost *:80>
  ServerName www.example1.com
  ServerAlias example1.com
  ServerAdmin [email protected]
  DocumentRoot /var/www/web1/web/

  <IfModule mod_fcgid.c>
    SuexecUserGroup web1 web1
    PHP_Fix_Pathinfo_Enable 1
    <Directory /var/www/web1/web/>
      Options +ExecCGI
      AllowOverride All
      AddHandler fcgid-script .php
      FCGIWrapper /var/www/php-fcgi-scripts/web1/php-fcgi-starter .php
      Order allow,deny
      Allow from all
    </Directory>
  </IfModule>

  # ErrorLog /var/log/apache2/error.log
  # CustomLog /var/log/apache2/access.log combined
  ServerSignature Off

</VirtualHost>

a2ensite web1
vi/etc/apache2/sites-available/web2
<VirtualHost *:80>
  ServerName www.example2.com
  ServerAlias example2.com
  ServerAdmin [email protected]
  DocumentRoot /var/www/web2/web/

  <IfModule mod_fcgid.c>
    SuexecUserGroup web2 web2
    PHP_Fix_Pathinfo_Enable 1
    <Directory /var/www/web2/web/>
      Options +ExecCGI
      AllowOverride All
      AddHandler fcgid-script .php
      FCGIWrapper /var/www/php-fcgi-scripts/web2/php-fcgi-starter .php
      Order allow,deny
      Allow from all
    </Directory>
  </IfModule>

  # ErrorLog /var/log/apache2/error.log
  # CustomLog /var/log/apache2/access.log combined
  ServerSignature Off

</VirtualHost>

a2ensite web2
Make sure you fill in the right paths (and the correct user and group in the SuexecUserGroup line).
Reload Apache afterwards:
/etc/init.d/apache2 reload