Separating Apache and PHP has some advantages. Faster delivery of non-PHP content, lesser load on the servers to name just the most obvious ones. That’s why you can find this kind of setups mostly at smaller ISPs. To really benefit from the separation of Apache and PHP, however, you’d need to move PHP to completely seperate servers.
That’s where FastCGIExternalServer comes into play. In theory this Apache configuration directive allows you to specify an external server where all requests for PHP scripts (or any other resources you define) will be sent to. But where you’ll find plenty of documentation about how to install a FastCGI server on the local machine, the documentation about how to use an external server is almost non-existant. And the official documentation doesn’t really help and neither does the FAQ. Or would you find this helpful:
What is the path used with FastCGIExternalServer?
Since all FastCGI directives are global (they are not configured in a server context), all FastCGI paths map to the filesystem. In the case of external servers, this path does not have anything to do with the file system; it is a virtual file system path. Since the connection between mod_fastcgi and the FastCGI app is by a socket (unix or tcp), mod_fastcgi does not care where the program is (it could be on a completely different machine). However, mod_fastcgi needs to know when a hit is calling for an external app, so it uses this path as if it were a local filesystem path. Apache translates a request URI to a filesystem path.
Most ironcally, it’s exactly the path mentioned above that causes most users to give up on FastCGIExternalServer. Ok, so let’s get our hands dirty. Follow me into the abyss of weird logic and software bugs to finally get your external PHP server up and running. On the way you’ll learn that nothing is as it seems and that the FAQ entry quoted above is misleading at best.
Ok, before we start I should mention what I’m using:
- Apache 2.2.11
- PHP 5.2.9 (compiled with –enable-fastcgi)
- mod_fastcgi
How to compile PHP as an FastCGI server is left as an exercise to the reader. Actually, mostz of the times it’s really as simple as adding --enable-fastcgi to the configure command and making sure that --enable-discard-path is not on the list of your configure options.
Compiling mod_fastcgi is straight forward and well documented, so it’s also not covered here.
Obviously you shouldn’t try the dollowing examples on a production machine. Ok, having all the mumbo-jumbo out of the way now, let’s begin.
A simple example
In the following examples I’m going to assume a few things:
- All software is installed in
/opt - You’re able to add and change virtual hosts at will
- We’ll use some directories in
/srv/www - The Web server is on the machine with the IP address 192.168.1.100
If that’s not the case on your system, make sure to adapt the examples according to your setup.
To enable mod_fastcgi add this line to your httpd.conf:
LoadModule fastcgi_modules modules/mod_fastcgi.so
Also make sure to comment all lines loading libphp5.so.
For the first test, let’s create a new virtual host:
<VirtualHost *>
ServerName fcgi.test.local
FastCGIExternalServer /srv/www/fcgi -host 127.0.0.1:9000
DocumentRoot /srv/www/fcgi
<Directory /srv/www/fcgi>
AllowOverride All
Order Allow,Deny
Allow from all
</Directoy>
</VirtualHost>
As you can see, in the first example the path option for the FastCGIExternalServer directive is exactly the same as the DocumentRoot.
By doing this, you’re basically telling Apache, that everything that resides in this very directory should be handled by mod_fastcgi which in turn will pass it on to the external server we specified.
So, let’s check it out.
First we need to create our example page:
mkdir -p /srv/www/fcgi echo "<?php phpinfo(); ?>" > /srv/www/fcgi/index.php
Also it helps to start our PHP FastCGI server:
export PHP_FCGI_CHILDREN=64 export PHP_FCGI_MAX_REQUESTS=2345 /opt/php-fcgi/bin/php-cgi -b :9000 &
What we’re doing here is to tell PHP that it’s got to start 64 independent servers waiting to serve requests. After having served 2345 requests any of these servers will quit and restart to prevent our server machine from dying of memory leaks still being caused by PHP.
As a last step, we need to tell our client machine where to find the server “fcgi.test.local”. This is done by adding a line to the file /etc/hosts if you’re on Linux or Mac OS or to C:\Windoze\System32\drivers\etc\hosts if you’re on a less desireable OS:
192.168.1.100 fcgi.test.local
Make sure to replace the 192.168.1.100 with the actual IP address of the web server you use for testing.
First Failure
Now that we’re all set, you can try to request http://fcgi.test.local in your browser.
This will most likely result in an error message:
No input file specified
Don’t worry, we’re going to deal with this problem later. For the moment, just request http://fcgi.test.local/index.php from your browser. You should see the familiar page with all the information PHP spits out when requested to do so.
Actually, it’s almost the familiar page. Most notably, the section “Apache Environment” should be missing. If you still see the Apache environment, you’re not running via FastCGI and you should check your setup before proceeding.
If everything worked as expected, you may now think “Hey cool. That was simple.”. Unfortunately it isn’t and I’ll show you why.
Remember the little line in our virtual host configuration, that enabled the external processing, supposedly of PHP scripts? I’ll repeat it here for clarity:
FastCGIExternalServer /srv/www/fcgi -host 127.0.0.1:9000
Remember also that I said this will cause all files residing in (or below, for that matter) that path will be processed by the external FCGI server? Unfortunately this is true to the word. Every file in or below that path will be processed by the external server. No matter whether it’s a PHP script (what we want) or a JavaScript file, a CSS file or some kind of graphics format. Everything will be processed by — well, thrown at — the external server.
Obviously, the PHP interpreter knowns nothing about interpreting JavaScript, CSS, GIF, JPG, you-name-it. You can easily test this by putting a GIF or JPG file into the directory and requesting it from withing your browser. The output will look something like this:

If you want to test for yourself, just copy any graphics file to the document root of your testing vhost and request it from within your browser. Clearly not what we want. But how to get to the desired result? That’s where the brain mangling starts.
Apache to the rescue!
Clearly we need a way to seperate the PHP code from all the other stuff in and below the document root. There’s a nice Apache config statement that allows us to du just that: AddHandler. So let’s modify our virtual host config a bit:
<VirtualHost *>
ServerName fcgi.test.local
FastCGIExternalServer /fcgi/www/fcgi -host 127.0.0.1:9000
AddHandler php-fastcgi .php
DocumentRoot /srv/www/fcgi
<Directory /srv/www/fcgi>
AllowOverride All
Order Allow,Deny
Allow from all
</Directoy>
</VirtualHost>
Now, what happened here? First, we changed the (not so virtual) path for the FastCGHIExternalServer to point to a new – as of yet non-existant – directory. Thus, the external PHP server will for the moment never be called and therefor it won’t interfere with graphics, CSS and other stuff.
Also, the new AddHandler directive instructs Apache to call the Action named php-fastcgi for every file with an extenstion of .php. So obviously we have to provide this handler using the Action directive.
Action, please!
Using the Action keyword you can instruct Apache to call whatever action you wish. So let’s modify our virtual host yet again:
<VirtualHost *>
ServerName fcgi.test.local
FastCGIExternalServer /fcgi/www/fcgi -host 127.0.0.1:9000
AddHandler php-fastcgi .php
Action php-fastcgi /virtualpath
DocumentRoot /srv/www/fcgi
<Directory /srv/www/fcgi>
AllowOverride All
Order Allow,Deny
Allow from all
</Directoy>
</VirtualHost>
This will tell Apache that whenever the action php-fastcgi is to be executed, the script /virtualpath should be run with the needed additional information passed to it via environment variables.
Ok, don’t bother with the fact that Apache wants to start a script, we’re going to deal with this in a minute. The important thing here is that /virtualpath really is virtual in the sense that it should not exist in the file system.
Looking at what we have right now you’ll most likely think that we’re even farer away from the goal than when we started. You’re right there, and now we’re going to fix that.
Alias me
To make the thing fly work, we need to make one last modification to our virtual host configuration:
<VirtualHost *>
ServerName fcgi.test.local
FastCGIExternalServer /fcgi/www/fcgi -host 127.0.0.1:9000
AddHandler php-fastcgi .php
Action php-fastcgi /virtualpath
Alias /virtualpath /fcgi/www/fcgi
DocumentRoot /srv/www/fcgi
<Directory /srv/www/fcgi>
AllowOverride All
Order Allow,Deny
Allow from all
</Directoy>
</VirtualHost>
The Alias directive instructs Apache to remap every call to /virtualpath to the new location /fcgi/www/fcgi and of course append all the stuff that was trailing the original path before. And alas, we now have a clean call to where our FastCGIExternalServer resides.
Recap:
To seperate our PHP code from the other stuff under the document root, we need to point the FastCGIExternalServer to some directory other than the original document root. To get to that very directory, we need to add a new Handler with AddHandler so Apache knows that files with the extension .php are to be treated specially. Next we have to create the Action we named as the handler for our PHP code. And finally we have to redirect the action to where our new FastCGIExternalServer thinks is his area of responsibility.
Do you spot the flaw?
Ok, that was easy: The (again, not so) virtual path for the FastCGIExternalServer doesn’t exist yet. So let’s fix that one, too:
mkdir -p /fcgi cd /fcgi ln -s /srv/www
This will provide a symbolic link to our document root. The only reason for having it really is the need to seperate our PHP code from all the other stuff. So essentially we came back full circle here just to make Apache and FastCGIExternalServer happy. Funny, isn’t it?
For must use cases, we’re finished here. However, there are some PHP apps out there that use deprecated internal variables and will refuse to work even after we fixed up almost everythin for them. To fix those remaining apps, too, we unfortunately need to modify either the app or PHP itself. So let’s deal with those, too.
Last Fixups
The remaining problem is that PHP has a little issue with translating the path information it gets when running as an FCGI server. This results in $_SERVER['SCRIPT_NAME'] being set to a wrong value. To fix this, we unfortunately have to patch PHP itself, namely the file sapi/cgi/cgi_main.c. There, after line 976, add this:
if (env_redirect_url &&
strncmp(env_server_software, "Apache", sizeof("Apache")-1) == 0) {
/*
* If we have an env_redirect_url and the web server is Apache
* it's very likely that env_redirect_url is the one we really
* want
*/
env_script_name = env_redirect_url;
}
This will make sure that the superglobal variable is set to the right value when using Apache 2.2 as your FastCGI frontend server. The patch has been tested with PHP 5.2.9 and 5.2.10 but might work with other versions as well.

Pingback: External FastCGI With Apache « IT Know-It-All
Great article, but how did you solve the problem with the trailing index.php?
If I call the domain, the php file are provided as a download.
If I add index.php, I get redirected to /virtualpath/index.php/ which results in a 404. Everything else works like a charm…
Can’t really say what the problem with your setup is. Either it’s a missing “DirectoryIndex index.php” statement in the htpd.conf file or your settings for calling the FCGI process are a bit borked. If you could post the settings for your vhost, maybe I could help more then.