Web Design

ETags, Gzip, & Expires Headers with YSlow

Our greatest weapon against the admonitions of YSlow is .htaccess, that innocuous text file that has the capacity to blow up your website.

For those of us cheapskates on shared servers, our greatest weapon against the admonitions of YSlow is .htaccess, that innocuous text file that has the capacity to blow up your website if you mistreat it.

To defeat rules #3: Add an Expires or Cache-Control Header, #4 GZip Components, and #13 Configure ETags, we need the following .htaccess swank (thanks to Matt Robertson for noticing that the rules I had here were not compressing CSS or JS):

GZip Compression

In the above, I use mod_deflate compression because I’m hosted on MediaTemple, which doesn’t offer gzip compression. The rules turn on mod_deflate for my CSS and Javascript because I’m also using WP Super Cache, which handles compression for everything else. In tandem with WP Super Cache, everything on my site is gzipped, and therefore YSlow gives me an A for rule #4.

If you don’t have the mod_deflate module on your shared space, you likely have mod_gzip enabled, so use that:

This won’t work on a variety of ancient browsers (pre-IE6, Opera 5, and Netscape, for example), but of course, who the hell cares.

Expires Headers

The idea here is that if you tell the browser the lifespan of your assets, the browser will keep them in its cache longer, which means your readers will be able to browse faster, from page to page, after they’ve downloaded your assets after the initial page load. Sounds a bit sketchy at first, but then I remembered that most people never CTRL+click Refresh or bother clearing their cache, so we can rely on their laziness.

My example above expires just about any kind of asset 1 week after it’s downloaded, but keep in mind that you have a lot of options when it comes to assigning these lifespans. It really depends on how often you change your content on your end.

Assigning Dynamic Images Cache Control Headers in FLIR

After much Interweb soul-searching, I decided that FLIR, the Facelift Image Replacement Method, is the best image replacement method out there (for my purposes, at least). I used sIFR 3 for a long time to render fancy fonts on my site, but every day when I looked at myself in the mirror, I was ashamed, among other things, for having to resort to Flash for such a trivial task. I intend to write an article about image replacement methods and the reason why I ultimately decided upon FLIR, so more on this later, but the point of my bringing this up is that the Facelift Image Replacement WordPress Plugin (which forks off the real FLIR) generates dynamic images for fonts, but doesn’t assign them cache-control headers.

Hours of Googling landed me on this page. Here the author provides a solution to the problem. He writes:

New user visits a brand new uncached image, it is generated. They refresh the page, another request is probably made and a 304 not modified is returned. You’re right, this is unnecessary.

I think by adding a cache-control to the output_file function the images would then get cached by the browser without causing an extra call to verify that it hasn’t been modified. This should stop a lot of unnecessary trips to the server.

The corresponding file in the Facelift plugin is inc-flir.php, located in the plugin’s /facelift/ folder. Scroll down to line 395, and add the following:

Adjust the expiration time to your liking. Now YSlow won’t complain that the dynamic images generated by FLIR have no cache-control headers.


Last but not least (well yes, maybe least), the mystical “entity tags.” These little things creep me out like Leeuwenhoek’s animalcules or FMA‘s homunculi. To paraphrase Yahoo, Etags are just doodads that servers and browsers use to check if the assets in the browser cache match up with original assets from the server. In the performance rules, Yahoo gripes about ETags because they don’t quite do what they’re supposed to do if your site pulls its assets from multiple servers. Of course, the vast majority of websites aren’t doing this because the vast majority of website owners are cheapskates. Nevertheless, YSlow will give us a big fat F if we try to actually configure ETags “correctly.” Since there’s little space left in our brains to debate such nonsense, Yahoo instructs us to simply turn the little entities off. And frankly, my dear, I don’t give a damn.

YUI Performance Rules

Mastering Yahoo's Performance Rules

Post a Comment

Your Two Cents


Your Comments

20 Replies & Counting

  1. Ellis Benus 9 years, 3 months ago

    Great article. Exactly what I was looking for. yslow should provide this information naively. thanks again.

  2. Jennifer Finch 9 years, 3 months ago

    Thank you so much for this article. I have been reading forums and FAQs all day and this is by far the clearest message out there. JF

  3. dquinn 9 years, 3 months ago

    Thanks! I’m glad these helped.

  4. mark robertson 9 years, 3 months ago

    Hey there. This is great. Im also on mediatemple with wordpress and was a bit confused regarding what htaccess rules to use. Would you mind helping me with my htaccess? Happy to donate.. Or, could you just clarify if we are supposed to use

    “mod_gzip_on Yes
    mod_gzip_dechunk Yes
    mod_gzip_item_include file .(html?|txt|css|js|php|pl)$
    mod_gzip_item_include handler ^cgi-script$
    mod_gzip_item_include mime ^text/.*
    mod_gzip_item_include mime ^application/x-javascript.*
    mod_gzip_item_exclude mime ^image/.*
    mod_gzip_item_exclude rspheader ^Content-Encoding:.*gzip.*”

  5. dquinn 9 years, 3 months ago

    Hi Mark – MediaTemple uses mod_deflate, so you’ll want to use:

    SetOutputFilter DEFLATE

    …instead of the gzip rules you cited, at the top of your .htaccess file.

  6. mark robertson 9 years, 3 months ago

    Thanks dquinn… does it have to be at the top? Any chance you want to make a quick $50 to take a look at my htaccess and fix?

  7. dquinn 9 years, 3 months ago

    Hey Mark, why don’t you post the .htaccess here? I don’t really have a lot of time to do small jobs these days, but I don’t mind taking a look.

  8. AskApache 9 years, 3 months ago

    Cool, just make sure that you tighten up which directives apply to which files, or else your site will end up sending out all kinds of headers.. e.g., the

    Header append Cache-Control “public”

    would apply to every request resulting in a server response of your site.. Lock it down and you are good to go!

  9. Will 9 years, 3 months ago

    Do the sequence of the htaccess directives matter?

  10. dquinn 9 years, 3 months ago

    Not with the rules I posted above, but it might if you have other directives going on.

  11. Ricky 9 years, 3 months ago

    Excellent article , this is exactly what i was looking for. Thanks.

  12. huangyq23 8 years, 6 months ago

    The syntax highlighter plug-in you’re using has a little bug too.

    It uses
    in CSS file to toggle the tool bar.
    But this could cause a Flash reloading in some browsers.

    Chang that to
    visibility: hidden;
    visibility: visible
    would do the trick.

    • Rush 8 years, 6 months ago

      Hi there, why do I get a 500 internal server error when I put these rules in my .htaccess?


    • Daniel Quinn 8 years, 6 months ago

      Any number of reasons. It’s possible you mistakenly copied something inappropriate into the .htaccess and it’s freaking out over syntax. It’s also possible your server doesn’t support MOD_DEFLATE or gzip.

  13. Boni 8 years, 5 months ago

    Really good article, i really don’t know how to configure about etags…
    Thanks for the article.

  14. Dennis 8 years, 4 months ago

    is there a way we can use
    header set last-modified “Sun Jul 18 00:32:50 EDT 2004”
    with today’s date directly in .htaccess file? My application is not based on php, so i can not use the trick you mentioned.

  15. Andre 8 years, 1 month ago

    Oh man! thank you so much for this!

  16. Chris Countey 7 years, 8 months ago

    Thanks for the information! On a side note, I noticed you have 40 H1 tags on this page. Is there a method to your H1 madness?


    • Daniel Quinn 7 years, 8 months ago

      My site is written in HTML5, and in the HTML5 spec, every section has its own heading which begins at h1.