How to prevent WP-Cache from changing a Feed’s Content-Type to text/html

Posted on February 18th, 2007 by Reiner.
Categories: English, Computers.

After upgrading to WP 2.1, Kelson had to downgrade WP-Cache 2.1 to a tweaked 2.0.17, because with RSS feeds WP-Cache 2.1 would,  when serving a request from its cache, always return Content-Type text/html instead of Content-Type text/xml.

Why does WP-Cache not reproduce the original Content-Type text/xml?

I had a look at wp_cache_shutdown_callback within wp-cache-phase2.php and found that all and everything should work perfectly.

Disabling WP-Cache’s code that forcibly sends a Content-Type text/html header if there is none, led me to the mazes ob flush(), ob_flush(), ob_end_flush(), headers_sent() and the like. And to the interesting fact, that…

WP-Cache does not see at least some of the http response headers, that have been output by WordPress code. Why?

If WP-Cache misses some headers, the reason must be located somewhere in the way it tries to retrieve them.  wp_cache_get_response_headers() first interrogates apache_response_headers() and, if this function does not exist, reverts to headers_list().

headers_list() docs clearly state, that it returns both, headers that have already been sent and those that have not yet been sent, i.e. are about to be sent. That might be the ones, that WP-Cache is missing after all. headers_list() requires PHP >= 5, but even then, headers_list() may not work at all due to Bug #29683 headers_list() func. return empty array.

apache_response_headers() requires PHP >= 4.3 (>= 4.3.3 with NSAPI)  and its docs are a little bit vague on platform requirements. There are some  hints on how to have it “working good”.

  • For apache_response_headers() working good, you need to set up output_buffering = Off in php.ini. Ok, I’ve checken on that one, it’s still set to its default = off.
  • If apache_response_headers() returns an empty array, try calling flush() before and it’ll get filled.  Bingo :-)   At least on my system using the Vistered Little Theme. Strange though, that it was just the Content-Type header, that was missing, the other ones were included even without the flush()…

Fix for wp_cache_get_response_headers within wp-cache-phase2.php:

function wp_cache_get_response_headers() {
  if(function_exists('apache_response_headers')) {
    flush(); // fix text/html on feeds
    $headers = apache_response_headers();
  } else if(function_exists(’headers_list’)) {
    $headers = array();
    foreach(headers_list() as $hdr) {
      list($header_name, $header_value) = explode(’: ‘, $hdr, 2);
      $headers[$header_name] = $header_value;
    }
  } else
    $headers = null;              

  return $headers;
}

Last-not-least an honorable mention of ieHTTPHeaders , that made it possible to spy on both the actual http request and response headers and much more. Thanks to Jonas!

There’s another interesting area as to when a page needs refreshing. It’s the If-Modified-Since header. Do WordPress and WP-Cache support this technique, and why not? I’ll try to cope with that one in a future post…

13 comments.

WordPress Broken on PHP 5.2 Again | K-Squared Ramblings

Pingback on February 21st, 2007.

[…] 6: On a related note, I tried Reiner Saddey’s fix for getting WP-Cache to remember the content-type on feeds, but it had the same problem my fix did, […]

Reiner

Comment on February 21st, 2007.

I promise to have in depth look into the 304 Not Modified till the end of next week. I just love Ricardo’s WP-Cache plugin, it’s well structured and I don’t think that it will be very hard to have it generate the proper 304 response.

Reiner Saddey’s Place » WP-Cache 2.1 revisited: Have x-gzip and gzip compression share same cache slot

Pingback on April 10th, 2007.

[…] working on the 304 not modified case that causes excessive traffic for RSS feeds, I encountered a mild weakness within the compression code, that causes both gzip and x-gzip […]

Webrocker » Feed-Hygiene und WP-Cache

Pingback on April 29th, 2007.

[…] [Nachtrag] Nicht ganz Scotty, aber der hier kommt verdammt nah dran: How to prevent WP-Cache from changing a Feed’s content-type… […]

Webrocker

Comment on April 29th, 2007.

Your fix didn’t work for me, but pointed me to the right direction :-)
wp-cache-phase2.php, line 230ff:

if (!$response{’Content-Type’}) {
$value = “text/html; charset=\”" . get_settings(’blog_charset’) . “\”";
@header(”Content-Type: $value”);
array_push($wp_cache_meta_object->headers, “Content-Type: $value”);
}

checks for “Content-Type”

BUT

wp-rss2.php sets the content-type header like this:
“Content-type”

So the check fails, resulting in the text/html content-type inserted by wp-cache.

Simply change “Content-type” to “Content-Type” in wp-rss2.php
on line 8:

header(’Content-Type: text/xml; charset=’ . get_option(’blog_charset’), true);

fixed the problem for me.

Reiner

Comment on April 29th, 2007.

Thanks Webrocker. Yes, I ran into that one as well, but ígnored it for the time beeing, falsely assuming, that all of WP code would use consistent casing.

The ultimate cause for this problem is, that according to HTTP specs, HTTP headers must be interpreted ignoring case. So Content-Type and Content-type are both valid headers, but the current wp-cache code compares them using “proper” case.

I’m terribly late doing the 304 thing (i.e. conditional get) :-(

Webrocker

Comment on April 29th, 2007.

it’s better to change the check in wp-cache-phase2.php than to change Wordpress-Core files:

if (!$response{’Content-Type’} && !$response{’Content-type’} && !$response{’content-type’}) {
$value = “text/html; charset=\”” . get_settings(’blog_charset’) . “\””;
@header(”Content-Type: $value”);
array_push($wp_cache_meta_object->headers, “Content-Type: $value”);
}

Webrocker

Comment on April 30th, 2007.

Hi again,
I changed the if-loop rather, uhm, unelegantly:

if (
!$response{’Content-Type’} &&
!$response{’Content-type’} &&
!$response{’content-type’} &&
!$response{’CONTENT-TYPE’}
) { … }

and now it works for my setup. I’m not very skilled with PHP, so maybe
there’s a better way to ‘check’ for the ‘content-type’
case-insensitive? Same would be true for the other if-loops, like
‘creation-date’ and such…
What is the problem with the ‘conditional-get’ thing?
If I look at the headers being sent (with the ‘LiveHTTPHeaders’
Extension for Firefox), I see 200 and 304 states, but I haven’t figured
out which one is the correct one…?
Thanks for your work and time,
Tom

_ck_

Comment on May 5th, 2007.

Thanks for spotting and reporting this. I want to add there is still a problem for me even with wp-cache 2.1.1 + php 4.4.6 with the special condition of LiteSpeed replacing Apache.

I suspect it’s a bug in how apache_response_headers() hooks back into LiteSpeed’s emulation which I will report to them. But you saved me a shedload of time tracking down the likely issue, very grateful.

Apache Guru

Comment on May 29th, 2007.

Very interesting topic, I personally just modified wp-rss2.php, but couldn’t you also just tell wp-cache not to cache wp-rss in the settings? If you did that you could just implement server-side Caching with apache.

Improving WP Super Cache? gzip for logged in users | Tummblr

Pingback on November 6th, 2007.

[…] recompressed for each request). Very interested in what Donncha thinks about this. Also included Reiner Saddey’s fix to prevent WP-Cache from changing the content type of feeds to […]

WordPress Super Cache 0.1 at Holy Shmoly!

Pingback on November 6th, 2007.

[…] all the bugs mentioned below are now fixed. I applied Tummbler’s patch (from Elliott and Reiner) that enables gzip compression of the WP-Cache data files and fixes feed content […]

WordPress Super Cache 0.2 WordPress Plugin » D’ Technology Weblog: Technology, Blogging, Tips, Tricks, Computer, Hardware, Software, Tutorials, Internet, Web, Gadgets, Fashion, LifeStyle, Entertainment, News and more.

Pingback on November 6th, 2007.

[…] Tummbler’s patch (from Elliott and Reiner) that enables gzip compression of the WP-Cache data files and fixes feed content types.Please note: […]

Leave a comment

Comments can contain some xhtml. Names and emails are required (emails aren't displayed), url's are optional.