The correct way to configure PHP [and why PHP.NET has it wrong]

DeveloperSide.NET Articles

Criticisms of the PHP.NET Manual on integrating PHP with Apache2, step-by-step...
Apache 2.0.x on Microsoft Windows

Using Apache 2.0 with PHP...

"Warning: We do not recommend using a threaded MPM in production with Apache2. Use the prefork MPM instead, or use Apache1."

This cryptic warning is in reference to the fact that not all 3rd-party PHP libraries are guaranteed to be thread-safe.

PHP itself and all of its core libraries are thread-safe.

This has nothing to do with stability or compatibility of Apache 2.0 with the PHP 'apache2handler' module.

The default MPM for Apache 2.0 is the process-based prefork module, and not the thread-based worker module.

Why this warning still exists and has existed for years without any further clarification is a mystery. It servers no purpose other than to confuse new users and cause headaches. No one I know has had any problems running PHP as a module under Apache2 -- for years.

For those still shaking like a leaf, the solution is to simply not use any PHP libraries that are not thread-safe -- most of which clearly state so if they fall into this category. Good luck finding one [at least one that is not obscure].

And the recommendation of using Apache1 over Apache2 under Windows is not much of a recommendation at all.

On to the rest of the Manual...

First, we will take a look at configuring PHP as a CGI binary, then PHP as an Apache2 module.

PHP as a CGI binary ['official' configuration]

ScriptAlias /php/ "c:/php/"
AddType application/x-httpd-php .php

# For PHP 4
Action application/x-httpd-php "/php/php.exe"

# For PHP 5
Action application/x-httpd-php "/php/php-cgi.exe"

Not only is this PHP CGI configuration incorrect in every possible way, it also makes absolutely no sense.

First lets look at the definitions of the Apache Directives used...

Directive: ScripAlias
Description: Maps a URL to a filesystem location and designates the target as a CGI script."
Syntax: ScriptAlias URL-path file-path|directory-path"

An example demonstrating the proper use of this directive would be...

ScriptAlias /cgi-bin/ "C:/www/cgi-bin/"

The above line would tell Apache to map URL '/cgi-bin/' to the system directory of 'C:\www\cgi-bin'.

Also note that the 'ScriptAlias' directive specifies to Apache that all files under this URL/directory target are cgi-scripts and should be handled accordingly; the scripts should be executed/parsed with the interpreter (binary) specified under the 'shebang' line (1st line) of the script file being served.

In other words, when a user accesses URL 'http://localhost/cgi-bin/file.cgi', the interpreter will process/parse file 'C:\www\cgi-bin\file.cgi' and Apache will serve the output to the user.

Directive: AddType
Description: Maps the given filename extensions onto the specified content type."
Syntax: AddType MIME-type extension [extension] ..."

'mod_mime' Summary:

"This module is used to associate various bits of "meta information" with files by their filename extensions. This information relates the filename of the document to its mime-type, language, character set and encoding. This information is sent to the browser, and participates in content negotiation, so the user's preferences are respected when choosing one of several possible files to serve. [...] In addition, mod_mime may define the handler and filters that originate and process content."

An example demonstrating the proper use of this directive would be...

AddType text/html .php

The above line would tell Apache to specify the 'content-type' field as 'text/html', under the generated header of the output, for *.php files.

Directive: Action
Description: Activates a CGI script for a particular handler or content-type."
Syntax: Action action-type cgi-script"

An example demonstrating the proper use of this directive would be...

To not use it at all in this context and replace it with 'AddHandler' directive.

On to the given PHP.NET configuration...

First directive...

ScriptAlias /php/ "c:/php/"

At first look the reader might assume that the sole purpose of this 'ScriptAlias' line is to map URL '/php/' to the system directory of 'c:\php\'.

Yet upon further inspection, it is brought to light that...

The true purpose of this particular 'ScriptAlias' line is to make the upcoming 'Action' line work by substituting '/php/' for 'c:\php\' under target '/php/php[-cgi].exe' and defining 'php[-cgi].exe' as a 'cgi-script' [more on this later].

When brought in combination with the 'Action' line, it makes no sense... And manipulates the definition of 'ScriptAlias' -- which simply maps a URL to a directory [or cgi file] and tells Apache that all files under this directory are scripts.

Not to mention that ScriptAlias'ing directory 'C:\php\', the directory that contains the PHP binaries and libs/dlls, is just downright stupid.

This is nothing more than a hack designed to work in combination with the upcoming directives.

Second directive...

AddType application/x-httpd-php .php

I really have an issue with this one.

The 'AddType' directive associates a content-type with a file extension for the benefit of the *client-side*. Its sole purpose is to identify the data stream (output) coming from the server, to the user's browser (client-side).

To be fair, it is true that...

"In addition, mod_mime may define the handler and filters that originate and process content."

Thought this is more of an addition than anything else, and creates more problems than it tries to solve (while other better solutions exist).

On first look it appears that the given configuration is using the 'AddType' directive to map a content-type of 'application/x-httpd-php' to a file extension of '.php'.

While this assumption is correct in its given purpose [the true purpose will be exposed by the following directive] and is certainly taking effect, it makes no sense...

The problem here lies in the fact that only the server processes/parses PHP files -- the user's browser does no such thing.

On a PHP request, the user might see a URL containing a file type of '.php', or any other type, or simply an ending '/'. And after the server processes/parses the requested PHP script, the output the browser receives IS ONLY HTML [content-type: 'text/html'].

While I'm not exactly sure what a content-type of 'application/x-httpd-php' specifies to the client-side; it is, never the less, now associated with a file extension of '.php'.

The purpose of this line is to associate a fake content-type of 'application/x-httpd-php' with a file extension of '.php' -- server-side.

Again, just another hack.

One last note to others and myself on this subject...

The various php-cgi configurations use handler methods, handler names, and content-types interchangeably. Under PHP, 'application/x-httpd-php' is a handler name under the php[4,5]apache2.dll 'apache2handler' modules, while also being a fake "handler" name and content-type under php-cgi. Also, php-cgi has [the so called] handler names 'php-script' (for php4) and 'php5-script' (for php5). Not to mention mod_cgi contains handler name 'cgi-script' and content-type 'application/x-httpd-cgi', both of which can be interchanged with the other handler names and content-types under php-cgi. Are you confused yet?

Third directive...

# For PHP 4
Action application/x-httpd-php "/php/php.exe"

# For PHP 5
Action application/x-httpd-php "/php/php-cgi.exe"

The 'Action' directive runs the specified 'cgi-script' when a given request for a URL contains the specified 'content-type' (or handler name). Do not confuse this 'cgi-script' with the actual document/request/URL. This 'cgi-script' receives the URL and file path of the requested document via the cgi environment variables passed to it.

In this case, the 'Action' line is telling Apache that anything of the fake server-side 'application/x-httpd-php' content-type should be passed to 'cgi-script' 'C:\php\php[-cgi].exe'.

How exactly (and why) the now translated 'C:\php\php[-cgi].exe' binary (URL '/php/php[-cgi].exe') is considered a 'cgi-script' is beyond even myself. But wait, I almost forgot. The previous 'ScriptAlias' line told Apache that everything under directory 'C:\php\' (URL '/php/') is now a script, including all the '*.exe' and '*.dll' files within. Finally, a reason to this madness!

Here is where the true purpose of the previous 'ScriptAlias' and 'AddType' lines are brought to light; and all the previous mistakes and incorrect uses are tied together to form a whole [mess].

The last field of the 'Action' directive, the 'cgi-script' field, "is the URL-path to a resource that has been designated as a CGI script using ScriptAlias or AddHandler."

While this particular use of the 'ScriptAlias' and 'Action' combination is questionable, the purpose is simple enough...

This particular use of the 'Action' directive requires this particular use of the 'ScriptAlias' directive, and this particular use of the 'ScriptAlias' directive prepares the use of this particular 'Action' directive. Is your head spinning yet?

Bringing it all together...

The 'AddType' line associates file extension '.php' with a fake content-type of 'application/x-httpd-php'; emphasis being on the server-side, with everything leaking to the client-side (user's browser).

The 'ScriptAlias' line maps URL '/php/' to directory 'C:\php\', with its damage being two fold...

The 'Action' line is relying on the mistakes, oversights, and incorrect use of the previous lines to bring everything together... By potentially executing all '.php' files, anywhere on the system, via the "cgi-script" 'C:\php\php[-cgi].exe'.

The damage here is offset by the fact that...

The rest of the baggage remains. And the user is kept oblivious to any of these issues.

Do take note that some of this baggage has been hidden away via more hacks under the PHP system with the purpose of making this configuration work and eliminating some of the obvious problems.

While all this does work, it still remains incorrect.

PHP as a CGI binary [the right way]

ScriptAlias /cgi-bin/ "C:/www/cgi-bin/"

<Directory "C:/www/cgi-bin">
	AllowOverride None
	Options None
	Order allow,deny
	Allow from all

That's it.

A simple 'ScriptAlias' for cgi-bin and a 'Directory' block to give permissions.

Every script just needs to have a 'shebang' line that points to the interpreter.

Aside from PHP, this also takes care of Perl and everything else.

Other configurations for php-cgi...

# General
AddHandler cgi-script .php
AddType text/html .php

PHP as an Apache2 module ['official' configuration]

# For PHP 4 do something like this:
LoadModule php4_module "c:/php/php4apache2.dll"
# Don't forget to copy the php4apache2.dll file from the sapi directory!
AddType application/x-httpd-php .php

# For PHP 5 do something like this:
LoadModule php5_module "c:/php/php5apache2.dll"
AddType application/x-httpd-php .php

# configure the path to php.ini
PHPIniDir "C:/php"

Previously, it was noted that the official 'PHP as a CGI binary' configuration was incorrect in every conceivable way, while at the same time making absolutely no sense.

Unfortunately, it appears that this observation also holds true for the official 'PHP as an Apache2 module' configuration, just to a lesser extent...

Directive 'AddType' associates a content-type with a file extension; identifying the data stream to the client-side (browser).

We have witnessed the same misuse of this directive before, and here that misuse is repeated again... Using a fake content-type to associate a file extension on the server-side.

The only difference here is that the loaded PHP module spots this 'content-type' and picks up the request [in very simplified terms].

Instead of using the 'AddType' directive [which is completely inappropriate and is no choice at all in this context], a better option would be the 'AddHandler' directive...

Directive: AddHandler
Description: Maps the filename extensions to the specified handler"
Syntax: AddHandler handler-name extension [extension] ..."

An example demonstrating the proper use of this directive would be...

AddHandler application/x-httpd-php .php

Directive 'AddHandler' maps a handler to a file extension for server-side processing of data (by Apache and modules).

The above 'AddHandler' line maps the 'application/x-httpd-php' handler [name], which is present under the PHP module, to the '.php' file extension.

Simple, and to the point.

The 'AddHandler' directive was introduced to Apache v1.1 in 1996.

The placement of php.ini

Nothing more remains to be said on the above PHP.NET configurations, except on the subject of php.ini...

This file has always been placed under the SYSTEM folder (C:\Windows or C:\Winnt).

These days, PHP.NET is recommending that users keep php.ini under the PHP folder, to, I assume, simplify the installation procedure for the average individual.

Even with a small matter such as this, making such recommendations and changes can have a profound effect.

And while reading this, do take into account that this is the same PHP.NET that once recommended moving all of the various PHP DLL files (including the dozens of odd extension dlls) into the user's SYSTEM folder (i.e. C:\Windows) to simplify the installation procedure. So do give credit where credit is due.

PHP as an Apache2 module [the right way]

#For PHP5
LoadFile "C:/www/php5/php5ts.dll"
LoadModule php5_module "C:/www/php5/php5apache2.dll"

<IfModule php5_module>
	#PHPIniDir "C:/Windows"
	#PHPIniDir "C:/Winnt"

	<Location />
		AddType text/html .php .phps
		AddHandler application/x-httpd-php .php
		AddHandler application/x-httpd-php-source .phps


#For PHP4
LoadFile "C:/www/php4/php4ts.dll"
LoadModule php4_module "C:/www/php4/php4apache2.dll"

<IfModule php4_module>
	#PHPIniDir "C:/Windows"
	#PHPIniDir "C:/Winnt"

	<Location />
		AddType text/html .php .phps
		AddHandler application/x-httpd-php .php
		AddHandler application/x-httpd-php-source .phps


In conclusion:

I have tried to locate the reasoning (and logic) behind the 'official' PHP configurations, without much success.

This is the only clear post that I have found on the subject [in reference to using 'AddHandler' over 'AddType' under the PHP.NET docs]...

List:       php-dev
Subject:    Re: [PHP-DEV] Why we dont_like_PHP
From:       Rasmus Lerdorf <rasmus () lerdorf ! com>
Date:       2004-12-20 22:08:01

"Like with most of the criticisms along these lines, they don't bother actually researching why something is a certain way. There was a time when there was no AddHandler directive. Addtype was the way you did this. Since AddType works with every version of Apache I never saw a reason to change this even after AddHandler was added since it is purely academic in nature."

Yes, that is it; nothing more. No reasons, no real explanations, nothing.

There will not be any changes...

I have said it before and I will say it again...

The 'AddHandler' directive was introduced to Apache v1.1 in 1996. The year now is 2006. Lets start using it.