PHP Bugs, Headers, and Such

Posted by Tom on January 10th, 2006

We just resolved a series of related bugs in the web product we’re developing. I’m hoping by documenting the symptoms and solutions it may ease someone else’s fruitless search.

The application has a “skin” feature allowing non-technical users to customize colors, font, and a handful of other things easily accomplished with CSS. Rather than edit more than twenty other pages to add an extra <link /> tag, we chose to add the following line to the top of our primary CSS file:

@import("custom.css") screen;

Notice how we defined the media type for the imported CSS file as part of the import rule, as suggested by the CSS2 specification.

A little more background: using a little bit of Apache magic courtesy of mod_rewrite in the httpd.conf, requests for the file custom.css are redirected to custom.php, in the same folder.

<Directory "/www/css">
   RewriteEngine on
   RewriteRule ^custom\.css$ custom.php [nc]
</Directory>

It didn’t work. Firefox’s Javascript Console was pointed directly to the problem: “The stylesheet http://localhost/css/custom.css was not loaded because its MIME type, ‘text/html’, is not ‘text/css’.” (Also, custom.css did not show up in Firefox’s built-in DOM Inspector.) The error was easily fixed, by adding the following line to custom.php:

header("Content-type: text/css");

The styles then loaded properly in Firefox (and Safari), but not in Internet Explorer. Browsing directly to custom.css worked fine in Firefox, but IE behaved oddly. It first acted as if it was about to download the file (oddly, the default action for the working CSS file), but then popped up a dialog saying the URL could not be found.

I then examined the HTTP headers, and compared them with a working file. (It’s off-topic for this post, but I stumbled across this while Googling for solutions. If you’re a geek and want to see some fun headers in the wild, visit Fun With HTTP Headers.)

Headers unique to main.css (working):

Last-Modified: Mon, 09 Jan 2006 22:28:54 GMT
ETag: "ce134-ed8-43c2e3a6"
Accept-Ranges: bytes
Content-Length: 3800
X-Pad: avoid browser bug

Headers unique to custom.css (not working):

X-Powered-By: PHP/5.0.4
Set-Cookie: PHPSESSID=uah38ktr3mgq3td5j6qmcdgjf3; path=/
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache

The cookie is generated automatically by PHP because of the need to use session information as part of the customization process. But, this is a CSS file, so I want it to be cached by default. Adding the following line before session_start() is called hid the no-cache headers, and things began to work in IE almost as they should.

session_cache_limiter("none");

I said, “Almost.” After tweaking the headers, I could now visit the file directly in IE without the “URL not found” error. (Meaning, it attempted to download it, rather than display it, but it was a step in the right direction.) However, it would still not apply the styles from the imported CSS file. It was difficult to debug as there were no error messages, and IE’s DOM tree is not consistent with W3C standards. The final piece—and I could find no documentation anywhere for this—was to strip the media type from the import statement (at the top of this post), making it:

@import("custom.css");

It sounds easy now that I write it, but this series of bugs cost several hours of work. I hope this post will save someone else similar trouble. (If you see something I missed, or know of a faster course we could have taken to solve this problem, feel free to add your comments!)

Add Your Comment