How To Set Up Apache2 With mod_fcgid And PHP5 On CentOS 6.2

Version 1.0
Author: Falko Timme
Follow me on Twitter

This tutorial describes how you can install Apache2 with mod_fcgid and PHP5 on CentOS 6.2. 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 CentOS 6.2 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.

Before we start, make sure that SELinux is disabled. Edit /etc/selinux/config and set SELINUX=disabled:

vi /etc/selinux/config
# This file controls the state of SELinux on the system.
# SELINUX= can take one of these three values:
#     enforcing - SELinux security policy is enforced.
#     permissive - SELinux prints warnings instead of enforcing.
#     disabled - No SELinux policy is loaded.
# SELINUXTYPE= can take one of these two values:
#     targeted - Targeted processes are protected,
#     mls - Multi Level Security protection.

Afterwards we must reboot the system:



2 Installing Apache2/mod_fcgi/PHP5

mod_fcgid is not available in the official CentOS repositories, but there's a package in the EPEL repository. We enable the repository as follows:

rpm --import
rpm -ivh epel-release-6-7.noarch.rpm

yum install yum-priorities

Edit /etc/yum.repos.d/epel.repo...

vi /etc/yum.repos.d/epel.repo

... and add the line priority=10 to the [epel] section:

name=Extra Packages for Enterprise Linux 6 - $basearch

Afterwards we can install Apache2, mod_fcgid, and PHP5:

yum install httpd mod_fcgid php-cli

If Apache2 was already installed with PHP5 as an Apache module, disable the PHP5 module now - open /etc/httpd/conf.d/php.conf...

vi /etc/httpd/conf.d/php.conf

... and comment out everything in that file:

# PHP is an HTML-embedded scripting language which attempts to make it
# easy for developers to write dynamically generated webpages.
#<IfModule prefork.c>
#  LoadModule php5_module modules/
#<IfModule worker.c>
#  LoadModule php5_module modules/
# Cause the PHP interpreter to handle files with a .php extension.
#AddHandler php5-script .php
#AddType text/html .php
# Add index.php to the list of files that will be served as directory
# indexes.
#DirectoryIndex index.php
# Uncomment the following line to allow PHP to pretty-print .phps
# files as PHP source code:
#AddType application/x-httpd-php-source .phps

Then we create the system startup links for Apache and start it:

chkconfig --levels 235 httpd on
/etc/init.d/httpd restart

Next we open /etc/php.ini...

vi /etc/php.ini

... and uncomment the line cgi.fix_pathinfo=1:

; cgi.fix_pathinfo provides *real* PATH_INFO/PATH_TRANSLATED support for CGI.  PHP's
; previous behaviour was to set PATH_TRANSLATED to SCRIPT_FILENAME, and to not grok
; what PATH_INFO is.  For more information on PATH_INFO, see the cgi specs.  Setting
; this to 1 will cause PHP CGI to fix its paths to conform to the spec.  A setting
; of zero causes PHP to behave as before.  Default is 1.  You should fix your scripts

Open /etc/httpd/conf.d/fcgid.conf...

vi /etc/httpd/conf.d/fcgid.conf

... and add the line PHP_Fix_Pathinfo_Enable 1 at the end:

PHP_Fix_Pathinfo_Enable 1

Then reload Apache:

/etc/init.d/httpd reload


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
chmod 755 /var/www/web1
chmod 755 /var/www/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/sbin/suexec -V

[[email protected] ~]# /usr/sbin/suexec -V
 -D AP_DOC_ROOT="/var/www"
 -D AP_GID_MIN=100
 -D AP_HTTPD_USER="apache"
 -D AP_LOG_EXEC="/var/log/httpd/suexec.log"
 -D AP_SAFE_PATH="/usr/local/bin:/usr/bin:/bin"
 -D AP_UID_MIN=500
 -D AP_USERDIR_SUFFIX="public_html"
[[email protected] ~]#

Therefore we cannot call the PHP binary (/usr/bin/php-cgi) 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/bin/php-cgi. 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/bin/php-cgi
vi /var/www/php-fcgi-scripts/web2/php-fcgi-starter
export PHPRC
exec /usr/bin/php-cgi

The PHPRC line contains the directory where the php.ini file is located (i.e., /etc/ translates to /etc/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 Add the following two vhosts at the end of /etc/httpd/conf/httpd.conf:

vi /etc/httpd/conf/httpd.conf
NameVirtualHost *:80
<VirtualHost *:80>
  ServerAdmin [email protected]
  DocumentRoot /var/www/web1/web/
  <IfModule mod_fcgid.c>
    SuexecUserGroup web1 web1
    <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
<VirtualHost *:80>
  ServerAdmin [email protected]
  DocumentRoot /var/www/web2/web/
  <IfModule mod_fcgid.c>
    SuexecUserGroup web2 web2
    <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

Make sure you fill in the right paths (and the correct user and group in the SuexecUserGroup lines).

Reload Apache afterwards:

/etc/init.d/httpd reload
Share this page:

Suggested articles

7 Comment(s)

Add comment



 Please check the link in the second step
 which is not working.

By: admin

I've corrected this, the new link is

By: Eddie

It is now epel-release-6-8.noarch.rpm

 Thanks - great tutorial.

By: Zen_nudist

Thanks a lot! It's a great tutorial!

By: Martin M. S. Pedersen

  Why do you want to disable SELinux? SELinux is important for a secure server.

By: till

Feel free to leave SELinux on if you want, you will have to create SELinux policies manually and activate them then for each website before you can fully use it.

By: ignorant

Appreciate your great tutorial. Will these work on Centos 7.4, Apache 2.4.6

I installed php 5.6.33 with FPM +