Frequently Asked Questions: CGI and PHP scripts

Can I run CGI scripts? Can I use PHP?

Yes! What's more, as of 28th September 2002 we have a brilliant new system in operation which allows even your PHP scripts to run as 'you' rather than as the webserver. Read on for details.

Where do I need to put my CGI/PHP files?

For individual users, PHP scripts must have a filename ending with ".php" and can be placed anywhere in your public_html directory. CGI scripts must have filenames ending ".cgi" and can also go anywhere in public_html.

For societies, both of the above methods also work, however there is an additional method of running CGI scripts which is to place them in the cgi-bin directory in your society user area (i.e. not in public_html). If you do this, the script names do not need to end ".cgi". The URL to access CGI scripts in the cgi-bin directory is http://SOCNAME.soc.srcf.net/cgi-bin, where SOCNAME is your society's name on the SRCF.

What unix user do scripts execute as?

For individual users, both PHP and CGI scripts will run as you, not as the web server's user id.

For societies, rather than running CGI/PHP scripts as an individual, we have introduced a unix user for each society. This user cannot log in, however it is used as the user under which all society PHP/CGI scripts are run. Outgoing email generated by society CGI/PHP scripts will, by default, appear to come from [socname]-webmaster@srcf.net. See Society email addresses.

Note that any CGI/PHP script which hogs the CPU for more than 2 minutes will be terminated by the system. This should not affect anything other than out-of-control scripts, as CGI/PHP scripts typically execute in a few seconds. It is a measure to prevent the server being excessively slowed down by buggy scripts which go into an infinite loop.

What permissions and ownerships do scripts need to have?

For individual users, CGI scripts must be readable and executable by you, and must be owned by you. PHP scripts must be world-readable (to keep database passwords secret, see the next question). For example:

pip$ ls -l
-rwx------  1 saw27  saw27  238 May  5 19:33 env.cgi
-rw-r--r--  1 saw27  saw27  265 May 13 19:34 phptest.php

Society CGI scripts and PHP scripts must have its group owner set to the society. CGI scripts must be group readable and executable. Society PHP scripts must additionally be world readable (to keep database passwords secret, see the next question). For example:

pip$ ls -l
-rwxrwx--- 1 saw27  casi  238 May  7 23:49 env.cgi
-rw-rw-r-- 1 saw27  casi  265 May 14 23:06 phptest.php

We recommend that you ensure that society files are group-writable, so that other admins of the society can edit them (not least, when your own account expires).

How do I keep database passwords used in PHP scripts secret from other users?

PHP scripts must be world-readable. This requirement is artificially imposed because we felt that if we didn't require world readability, users might be caught out by assuming that if something (other than a CGI script) is not world readable then it's not accessible on the web, which wouldn't be the case for PHP scripts. But it's easy to get round: put your secret information in a separate file which is not world readable (but is group readable), and include that file from your main PHP script.

Where do errors (STDERR) from my CGI scripts end up?

They are appended to Apache's main error_log: /var/log/apache2/error.log. (Your account has its own error log for web server messages but not CGI STDERR: /var/log/apache2/soc|user/USERNAME/error.log.)

My PHP script needs more memory!

By default we have a maximum memory limit of 64MB set for PHP scripts. You can override this by placing a file called php_override.ini at the top level of your site containing, for example, memory_limit = 128M.

Please consider the memory requirement of other users / system processes / etc. before doing this.

Is there something wrong with PHP session tracking?

Because PHP runs as 'you', not as the webserver, if you enable PHP session tracking, the temporary session files it creates are not writable by other users. As a result, if users visit two different user or society sites on the SRCF which both use PHP sessions, they'll get an error message.

The way to avoid this is for everybody to restrict their session cookies only to your own website (a good idea for security reasons in any case). Do this by putting the following line at the top of your PHP script:

ini_set('session.cookie_path','/~abc23/');

(for individual users, replacing abc23 with your userid), or

ini_set('session.cookie_path','/socname/');

(for societies), replacing socname with your society abbreviation.

You don't need to do this if you have your own virtually hosted domain name.

Unfortunately, even if you do this, users of your site may still see errors if they visit someone else's SRCF-hosted site which uses PHP session cookies (without the above ini_set) and then visits yours. A more advanced solution which stops this problem is to also set session.save_path to a directory within your society or user filespace (but not within public_html). This will store session files in the specified location, rather than in /tmp which is shared between all users.

For example, the society socname might create a directory /societies/socname/tmp with the shell command mkdir /societies/socname/tmp and then put

ini_set('session.save_path','/societies/socname/tmp');

and

ini_set('session.cookie_path','/socname/');

at the start of all its PHP scripts.

How do I send custom HTTP headers using PHP?

This question is often phrased as: I receive the "Internal Server Error" message (in the browser), and the following in the server log (/var/log/apache2/error.log):

[Thu Apr 15 13:35:51 2004] [error] [client XX.XX.XX.XX] malformed header from script. Bad header=HTTP/1.1 301 Moved Permanently: /usr/lib/cgi-bin/srcf-php-handler

The chances are that the php code you are using has not been designed to work with our version of PHP in "CGI mode" (which is how we do it on the SRCF), only when PHP is running as an Apache module.

In detail... the problem comes when your script effectively does:

header("HTTP/1.1 301 Moved Permanently");

(e.g. in line 240 of OutputPage.php).

This is a raw HTTP header. When PHP-running-as-an-Apache-module sees this function call with an argument starting "HTTP", it knows that it doesn't need to add it's own "HTTP" header as it usually would, and sends it on to the browser.

But when invoking any CGI process (including PHP-running-as-a-CGI-program), Apache *always* adds the "HTTP" line itself, and it is simply not part of the CGI specification for the CGI script to do that, unless the CGI script is invoked in "non-parsed header" mode (in which case the CGI script *must* provide the HTTP line itself). For regular CGI scripts, NPH mode is triggered if the script's filename starts "nph-"; this isn't relevant for PHP though.

The PHP documentation for header() at goes a little way to flagging this problem. When talking about using header("HTTP..."): it says "Note: In PHP 3, this only works when PHP is compiled as an Apache module." Further investigation has revealed that, although it does work in later versions of PHP 4, it also fails to work in the version of PHP 4 which the SRCF currently uses at the time of writing (May 2004).

Therefore, the PHP script you're using won't work in CGI mode with the SRCF's version of PHP.

All is not lost, though, since you can achieve exactly the same effect with:

header("Status: 301 Moved Permanantly");

The Status header is actually one of a few special cases (as defined in the CGI spec) which is not passed on to the browser directly but is used by the web server to generate a suitable HTTP line. So the effect to the browser is identical to header("HTTP/1.1 301 Moved Permanantly").

At the end of the day, then, you should be able to fix this and retain full functionality by changing all instances of "HTTP/1.1" in your PHP code to "Status:".

How do I turn CGI off so that people can download the file?

Several kinds of file will automatically be interpreted as CGI scripts, and so the CGI handler will try to run them when you visit their URL, even if you just wanted to download them. The following will turn off CGI handling for python scripts, displaying them as plain text instead:

AddHandler default-handler .py
AddType text/plain .py
Put those lines in a .htaccess file in the same directory as your python files, and they will no longer be considered CGI scripts. You can do a similar thing for other file types by changing the .py to, for example, .php.