How To Set Up Apache2 With mod_fcgid And PHP5 On Debian Lenny

Version 1.0
Author: Falko Timme
Follow me on Twitter

This tutorial describes how you can install Apache2 with mod_fcgid and PHP5 on Debian Lenny. 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 a Debian Lenny server in this tutorial with the hostname and the IP address

I will create two Apache vhosts in this tutorial, and, to demonstrate the usage of mod_fcgid.


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 rewrite
a2enmod suexec
a2enmod include
a2enmod 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 And

I will now create two vhosts, (with the document root /var/www/web1/web) and (with the document root /var/www/web2/web). will be owned by the user and group web1, and by the user and group web2.

First we create the users and groups:

groupadd web1
groupadd web2
useradd -s /bin/false -d /var/www/web1 -m -g web1 web1
useradd -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/web
chown web1:web1 /var/www/web1/web
mkdir -p /var/www/web2/web
chown 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

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"

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/web1
mkdir -p /var/www/php-fcgi-scripts/web2

vi /var/www/php-fcgi-scripts/web1/php-fcgi-starter
export PHPRC
exec /usr/lib/cgi-bin/php
vi /var/www/php-fcgi-scripts/web2/php-fcgi-starter
export PHPRC
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-starter
chmod 755 /var/www/php-fcgi-scripts/web2/php-fcgi-starter
chown -R web1:web1 /var/www/php-fcgi-scripts/web1
chown -R web2:web2 /var/www/php-fcgi-scripts/web2

Now we create the Apache vhosts for and

vi /etc/apache2/sites-available/web1
<VirtualHost *:80>
  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

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

a2ensite web1
vi /etc/apache2/sites-available/web2
<VirtualHost *:80>
  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

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

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
Share this page:

Suggested articles

15 Comment(s)

Add comment


By: Robert

Will "aptitude install apache2 apache2-suexec libapache2-mod-fcgid php5-cgi" install apache2-mpm-worker or apache2-mpm-event ?

And also, is HTTP Authentication now available when PHP is running in (fast)CGI mode ? (As apposed to PHP running as an Apache module.)

By: Robert

OK, I found the answer to my first question with the -sPD flags. Thank God mpm-worker is installed and not the experimental mpm-event.:

sudo aptitude -sPD install apache2 apache2-suexec libapache2-mod-fcgid php5-cgi

Reading package lists... Done Building dependency tree Reading state information... Done Reading extended state information Initializing package states... Done The following NEW packages will be installed: apache2 apache2-mpm-worker{a} (D: apache2) apache2-suexec apache2-utils{a} (D: apache2.2-common) apache2.2-bin{a} (D: apache2-mpm-worker, D: apache2.2-common) apache2.2-common{a} (D: apache2, D: apache2-mpm-worker, D: apache2-suexec, D: libapache2-mod-fcgid) libapache2-mod-fcgid libapr1{a} (D: apache2-utils, D: apache2.2-bin, D: libaprutil1) libaprutil1{a} (D: apache2-utils, D: apache2.2-bin, D: libaprutil1-dbd-sqlite3, D: libaprutil1-ldap) libaprutil1-dbd-sqlite3{a} (D: apache2.2-bin) libaprutil1-ldap{a} (D: apache2.2-bin) php5-cgi php5-common{a} (D: php5-cgi) ssl-cert{a} (R: apache2.2-common) 0 packages upgraded, 14 newly installed, 0 to remove and 0 not upgraded. Need to get 7,550kB of archives. After unpacking 18.6MB will be used. Do you want to continue? [Y/n/?] Would download/install/remove packages.

Any PHP programmers who can answer my second question about HTTP authentication ?

(It would be beneficial to others as well, who have scripts which depend on HTTP Authentication at some stage, to know this before installing PHP as (fast)CGI.)

By: Robert

Apparently one still has to use workarounds to get HTTP Authentication to work in (fast)CGI mode :

By: Anonymous

Thanks. I followed your HOWTO and it works to the point of calling a .php file. When browsing http://localhost/index.php, Firefox tells me "You have chosen to open index.php, which is a .php file", and asks me where to save it. It seems Apache does not properly handle the PHP. And idea?

By: traxxus

Same problem here. If i go to my browser wants to download the file. seems like problems with php.... i followed the tutorial step by step....

Please help.

By: George Negoita

Quote from mod_fcgid's documentation:

"By default, PHP stops accepting new FastCGI connections after handling 500 requests; unfortunately, there is a potential race condition during the PHP cleanup code in which PHP can be shutting down but still have the socket open, so mod_fcgid under heavy load can send request number 501 to PHP and have it "accepted", but then PHP appears to simply exit, causing errors."

This is solved by setting MaxRequestsPerProcess to the same value as PHP_FCGI_MAX_REQUESTS. So, for your example, you should specify a value of 5000 for MaxRequestsPerProcess (unless debian comes with a default setting of 5000 or, unless you're reading this how-to just for fun, without having the need for a stable setup).

You can test this with ab:

ab -n 10000 -c 200

You should get 0 failed requests.

By: ashish

yes it says 0 failed requests, but still my info.php is not getting opened up in the browser. It's there in a downloadable mode though.

By: ashish khurana

got it. You need to install "libapache2-mod-php5" too.


Well, actually, we did test this, and it was working for us. Maybe this has changed with recent Debian updates, but at the time of writing, it was working. We don't just copy old tutorials, we always test them, sometimes even twice or thrice, so your accusation is totally wrong and inappropriate.

BTW, if you have problems with a tutorial, please use the forum to ask for help. There's even this statement above the comment area: "Please do not use the comment function to ask for help! If you need help, please use our forum."

By: Webcyclopedie

Thanks for this, it works perfectly for me on Debian Lenny 5.0.4 64 Bits.

Keep your good work.

By: Anonymous

This article is absolutely worthless, and I'm disappointed that this site is even displaying it.

 It appears the author just copied, word for word, the same article for Debian Etch and claimed it worked for Debian Lenny. Well, it doesn't. 

 As others have pointed out, at the end of this article you end up with a server that isn't running php at all- instead it's just serving out the source code directly. Despite people bringing this up the original author hasn't bothered updating or even responding to the comments.

 This article should be erased until someone who is actually going to bother testing what they write gives it a go.

By: Heine Deelstra

Debian Lenny comes with a default vhost with /var/www as its document root.

 By accessing the server via this default vhost (using ip for example), it is then possible to download scripts from the other vhosts, by simple path traversal.

 The default vhost must be disabled.

By: Christoph

Hi, what file permissions are to be set for the directories up to the website directory? I had permanent "permission denied" errors until I made the directories world readable.

 But is this still safe?

 The problem is, that the Apache itself has to get to the web page directory, if he doesn't belong to the users group or if the permissions are not set world-readable it cannot get there.

 I think I maybe misunderstood something?


By: Anonymous

fcgid lack the external fastcgi server feature, which means you cannot connect to php-fpm, the official php solution for fastcgi. If you don't wanna use php-fpm, that's fine. you can use fcgid to manage the processes, but you will lose the benefits which php-fpm can bring you and let you enjoy fastcgi. e.g. fastcgi_finish_request(), let you finish the request and do other server-side work.

By: trebor


The line is no more OK in the new Apache2.4

"vi /etc/apache2/sites-available/web2"

You have to write it like ".../sites-available/web2.conf". The .conf is mandatory by the new apache!

AND you have to define the vhost with

"Require all granted" where this example

has writen:      Order Deny,Allow      Deny from all

I also have diesabled the Option

# PHP_FixPathinfo_Enable 1

then the website with phpinfo got visible by the browser!

But thanks for the description here! ;)