Recently while upgrading from PHP 5.3.6 to 5.4.0 on an Apache 2.2.22 server, I encountered a peculiar behavior where MultiViews content negotiation stopped working specifically for .php files, while continuing to work normally for other file types like .txt. Here's the complete technical deep dive:
# Error observed in Apache logs
Negotiation: discovered file(s) matching request: /home/server1/htdocs/admin/contents (None could be negotiated)
The virtual host was configured with MultiViews enabled:
<Directory "/home/server1/htdocs">
Options Indexes Includes FollowSymLinks MultiViews
Order allow,deny
Allow from all
AllowOverride All
</Directory>
Additionally, these rewrite rules were present in .htaccess:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([^\\./]*)$ index.php?t=$1 [L]
</IfModule>
After extensive testing, I discovered the issue stems from an interaction between three factors:
- The PHP handler configuration changed in the upgrade
- MultiViews negotiation priority
- RewriteRule interference
Option 1: Explicit Type Map
Create a type map file named contents.var
in the same directory:
URI: contents
URI: contents.php
Content-type: application/x-httpd-php
Content-negotiation: 1.0
Option 2: Force PHP Handler
Add this to your Apache config or .htaccess:
<FilesMatch "^contents$">
ForceType application/x-httpd-php
SetHandler application/x-httpd-php
</FilesMatch>
Option 3: RewriteRule Adjustment
Modify the existing rewrite rule to exclude negotiated paths:
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_URI} !\.php$
RewriteRule ^([^\\./]*)$ index.php?t=$1 [L]
- Always test content negotiation during PHP upgrades
- Consider using explicit rewrites instead of MultiViews for PHP files
- Monitor Apache error logs for negotiation warnings
When upgrading from PHP 5.3.6 to 5.4.0, many administrators encounter a peculiar issue where Apache's content negotiation suddenly stops working specifically for PHP files. The server logs show:
Negotiation: discovered file(s) matching request: /home/server1/htdocs/admin/contents
(None could be negotiated)
This behavior occurs when three key Apache modules interact:
LoadModule negotiation_module modules/mod_negotiation.so
LoadModule mime_module modules/mod_mime.so
LoadModule php5_module modules/libphp5.so
The issue manifests when all these conditions are met:
- MultiViews is enabled in Directory configuration
- The requested URI has no file extension
- Only .php files exist for the requested resource
First, verify your MIME type configuration:
# In httpd.conf or apache2.conf
AddType application/x-httpd-php .php
AddHandler php5-script .php
Then check the negotiation priority:
# Create a test file called type-map in your directory
URI: contents
URI: contents.php
Content-type: application/x-httpd-php
Content-language: en
Your existing rewrite rules might interfere with negotiation:
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([^\\./]*)$ index.php?t=$1 [L]
Try modifying to:
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_URI} !\.php$
RewriteRule ^([^\./]*)$ index.php?t=$1 [L]
The PHP 5.4 handler registration changed. Verify your configuration includes:
<FilesMatch \.php$>
SetHandler application/x-httpd-php
</FilesMatch>
If negotiation still fails, consider these approaches:
# Option 1: Force negotiation for PHP files
<Directory "/home/server1/htdocs">
Options +MultiViews
MultiviewsMatch Any
</Directory>
# Option 2: Explicit type map
<Files "contents">
ForceType application/x-httpd-php
SetHandler application/x-httpd-php
</Files>
Enable detailed logging:
LogLevel debug
NegotiationLog /var/log/apache2/negotiation.log
This will reveal why the negotiation fails specifically for PHP files while working for others like .txt.