404 errors in WordPress 3 theme files

Author: Andy Hartwell

In the move to WordPress 3, I’ve discovered an interesting, hitherto unknown “feature” which, if you’re building AJAX functionality into your themes or plugins, may cause a headache or two. It’s an easy fix, once you’re aware of the issue.

It seems any .php file which you refer to directly (like, say, ajax.php or dynamicscript.js.php) rather than using any URL rewriting (where URLs are routed through index.php) is given a 404 header, while the rest of the content stays in tact.

What this means is that, even though you can view the files in your web browser without issue, any AJAX calls will fail because the XML parser assumes the file doesn’t exist. This is also the case if you include a <script> tag whose src attribute is set to a dynamically-generated file. You can view the file in your browser, but it will be ignored when referenced on a page.

The fix is simple, but varies depending on whether you’re using AJAX or including a dynamic file via <script> or <link> tags. If using AJAX, you may be including the PHP script using <?php include... ?> or <?php require... ?> when the page first renders, then replacing the contents with something new when a button or link is clicked. Let’s say you have a calendar widget which loads a list of events. You click the calendar, and the list magically updates showing the new list of events for that date, without the page refreshing. There, the list is included through <?php require... ?> and then replaced when the jQuery AJAX request returns a response.

In that instance, you have to tell the difference between the first time the script is rendered, and the second time, when it’s called via AJAX.  You’ll have your own way of doing that no doubt (a simple way is to include an extra querystring parameter, like “?ajax=1“), so what you’ll need to do is include the following, only for AJAX responses (not the first time the page is rendered):

<?php header('HTTP/1.0 200 OK'); ?>

This has the effect of overriding WordPress’ decision, forcing PHP to return a 200 header, which is understood by the browser as being the response “yes, this file does exist”. (Thanks to Chris for working out the exact formulation of this statement.)

You can do exactly the same for dynamically generated JavaScript or CSS, without worrying about whether you’re rendering for the first time, or partially via AJAX.

I’ve not found any documentation to support an explanation of this new behaviour, but as far as I can tell it’s peculiar only to WordPress 3.x.