Thursday, October 16, 2008

Switching from ModPHP (on Apache) to PHP as CGI


Recently, due to various technical reasons, I had to make the switch from running ModPHP on Apache to running PHP as CGI, and it surely wasn't straightforward!

Php.net is great for looking up php functions but when it comes to configuration/architectural documentation it's a bit muddled. There is no clear documentation on how to get php running as CGI anywhere! The fact that it's done differently on PHP4 and PHP5 also lead to many wild goose chases.

Putting the following in your httpd.conf is absolutely the wrong way to go -

<Directory /path/to/docroot/>  Options +ExecCGI </Directory>

This makes all the php scripts in the document root be treated as executables. But it also requires that all php scripts be now made executable (chmod a+x blah.php) and the shebang line (#!/usr/bin/php) be added to the top of all of them which is clearly the wrong road to take. Ideally we should not have to change any sources at all.

In addition I learnt that I was required to emit out the correct http headers manually on top of every script. Too, doing header("Content-type: text/html"); does not work! I have to say echo "Content-type: text/html\r\n\r\n"; Which is a tad primitive. Note the doubled \r\n\r\n in the echo. It is mandated by the http protocol (headers have a blank line after them).

Finally I figured out a reasonable way of doing this -

First step is compiling PHP as CGI using configure options of --enable-force-cgi-redirect and --enable-discard-path so that the correct headers are emmitted automatically. Usually Linux distros come with php-cgi binary which has already been compiled in this manner so you don't have to do your own compilation, but I had to do this for technical reasons that I won't elaborate upon. The documentation on these two compile time options is spotty so I still don't know exactly what they do.

Now if you try to run this new binary with the same (wrong) httpd.conf settings I mentioned above you will get the following nice error -

Security Alert! The PHP CGI cannot be accessed directly.
This PHP CGI binary was compiled with force-cgi-redirect enabled. This means that a page will only be served up if the REDIRECT_STATUS CGI variable is set, e.g. via an Apache Action directive.
For more information as to why this behaviour exists, see the manual page for CGI security.
Premature end of script headers: blah.php

The problem is that Options +ExecCGI line causes all php files to be treated as executables. Which means that they don't pass through the apache redirect to action handler mechanism and the REDIRECT_STATUS variable is never set. This causes the above error, as "force-cgi-redirect" requires that variable to be set. To resolve that read on -

The second step is adding the following lines in your httpd.conf -
ScriptAlias /cgi-php/ /usr/local/bin/
AddType application/x-httpd-php .php
Action application/x-httpd-php /cgi-php/php-cgi
AddHandler application/x-httpd-php php
This will make all php files go through the apache redirect mechanism and the above error will go away. Yay!

I'm still not sure if I need all these lines in my httpd.conf but it works for me right now!