Leverage browser caching: How to add Expires headers

Posted by on Mar 29, 2012 in Performance | 102 comments

This tutorial explains how you add Expires headers to your .htaccess file. This will help you improve the performance of your website, based on Google’s and Yahoo’s recommended performance guidelines.

How to add expires headers to leverage browser cache

You’ll learn about:

  • what browser caching and Expires headers is
  • how to test the current performance of your site
  • how to add Expires headers for your website
  • where you can find more information
Important! To make use of Expires headers the way it’s explained in this blog post, your server must be Apache (and requires the module mod_expires) and you must have access to your .htaccess file. If you don’t know what this means, talk with your hosting company first.

What is browser caching and expires headers?

The point of using browser caching and expiry headers is to reduce the number of HTTP requests, which improves the performance for your returning visitors.

“…leveraging browser caching is a cross between giving your browser a better memory and a camera” (source: Distilled)

The first time someone visits your site, their browser will fetch all your images, css files, javascript files, etc. Normally that happens every time the same visitor comes back to your site.

With Expires headers you tell your website visitor’s browser that the files you specify are not changing until after a certain time, for example a month.

This means that the browser doesn’t have to to re-fetch images, css, javascript etc every time your visitor comes back to your site.

“If cached, the document may be fetched from the cache rather than from the source until this time has passed. After that, the cache copy is considered “expired” and invalid, and a new copy must be obtained from the source.” (source: Apache)


Check your current website performance

Before you start, test your current status with Google Page Speed tool and Yahoo Yslow.

I personally prefer using http://gtmetrix.com because it shows you both Google’s and Yahoo’s page speed tools. It also updates instantly, so you can get an updated result straight after you’ve implemented your changes.

If you use GTmetrix, you should now see something like this (example of a website with no performance improvements done):

GTmetrix website performance result before implementing Expires headers

Example of GTmetrix website performance result before implementing Expires headers

Look under the (Google) Page Speed tab, and you see Leverage browser caching:

“The following cacheable resources have a short freshness lifetime. Specify an expiration at least one week in the future for the following resources”

Under the Yslow tab, you see Add Expires headers:

“There are [x] static components without a far-future expiration date.”

Today, you are going to improve your result for both of the above.


How to add Expires headers

First, look at your results in GTmetrix – what type of files do you have listed under “Leverage browser caching” and “Add Expires headers”? I had the following types of files, and I think you might too:

  • images: jpg, gif, png
  • favicon/ico
  • javascript
  • css

Think about how often you change your different files, and then decide for how long they can be cached in your visitor’s browser. Your options are:

  • years
  • months
  • weeks
  • days
  • hours
  • minutes
  • seconds

Do not add anything in your htaccess file yet. First look at each file type below, and change the caching times to suit your website:

Expires header for your favicon

For files that very rarely change, like your favicon, you can set a very far future expiry date. The code for your favicon would look like this:

ExpiresByType image/x-icon "access plus 1 year”

This means it will be cached in your visitor’s browser for 1 year from the day of the first visit. If your website visitor clears the browser cache, it will re-fetch the resources again.

Expires header for your images

The existing images on my sites are rarely updated, but it does occasionally happen, so 1 month works for me:

ExpiresByType image/gif "access plus 1 month"
ExpiresByType image/png "access plus 1 month"
ExpiresByType image/jpg "access plus 1 month"
ExpiresByType image/jpeg "access plus 1 month"

Expires header for your css

I personally update my css once in a while, so I’ve chosen 1 month as a reasonable caching time:

ExpiresByType text/css "access plus 1 month”

Expires header for your javascript

My javascript is something I rarely even look at, so the caching time is set to 1 year.

ExpiresByType application/javascript "access plus 1 year"
Warning! If you set a far future expiry date for something and then update one of those files, you must change the name of the file for the browser to re-fetch it.

Example: if you set your javascript to 1 year, and you update one of your javascript files, you’d have to rename the actual file. A good way to do this is by versioning, i.e. myfile_v1.2.js, but the easier way is to be careful with your Expires headers (setting something to 10 years is never a good option IMO).

What to add in your .htaccess file

Open your .htaccess file. (be smart: make a copy of your original .htaccess file, in case you accidentally make a mistake and need to revert)

Now it’s time to enable the Expires headers module in Apache (set the ‘ExpiresActive’ to ‘On’), so add this to your .htaccess file:

<IfModule mod_expires.c>

# Enable expirations
ExpiresActive On 

</IfModule>

It might be useful to add a “Default directive” for a default expiry date, so that’s the 2 rows you’ll add now:

<IfModule mod_expires.c>

# Enable expirations
ExpiresActive On 

# Default directive
ExpiresDefault "access plus 1 month"

</IfModule>

That’s the base. Now add all the lines for each of your file types (you know, the ones you created earlier for your favicon, images, css and javascript). You’ll end up with a code snippet that looks something like this:

<IfModule mod_expires.c>

# Enable expirations
ExpiresActive On

# Default directive
ExpiresDefault "access plus 1 month"

# My favicon
ExpiresByType image/x-icon "access plus 1 year”

# Images
ExpiresByType image/gif "access plus 1 month"
ExpiresByType image/png "access plus 1 month"
ExpiresByType image/jpg "access plus 1 month"
ExpiresByType image/jpeg "access plus 1 month"

# CSS
ExpiresByType text/css "access 1 month”

# Javascript
ExpiresByType application/javascript "access plus 1 year"

</IfModule>

That’s it.

Now run another test with GTmetrix and compare the results. This is the result for my test site, after implementing Expires headers:

GTmetrix performance improvement after adding Expires headers

GTmetrix performance improvement after adding Expires headers. Google: +16%. Yahoo: +3%

Did you notice any improvements for your site? Did the above take care of all your files listed under “Leverage browser caching” and “Add Expires headers“? Let me know in the comments below.


Expires headers resources


Did you find this tutorial useful? Do you have questions or feedback? Let me know in the comments below.

Remember to share the knowledge: recommend this tutorial to your network on Twitter, Facebook, Google Plus, Pinterest, etc.

About

Tess is a location independent Swedish emarketing tigress, and the founder of For the Love of SEO and owner of JoomlaTips. Follow Tess on Twitter: @tessneale / @joomlatips / @fortheloveofseo and Google Plus: Tess Neale

102 Comments

  1. Great post! Very helpful. It boosted my yslow grade by quite a bit. Thank you for writing this article.

    • Great post, Tess, thank you. You’ve managed to make a complex subject understandable to non-experts.

      For effective browser caching, though, Google also recommend validation using Last-modified or Etag.

      Mark Nottingham’s ‘Caching Tutorial for Web Authors and Webmasters’ (May 2013), however, says that ‘most modern Web servers will generate both ETag and Last-Modified headers to use as validators for static content (i.e., files) automatically; you won’t have to do anything. However, they don’t know enough about dynamic content (like CGI, ASP or database sites) to generate them; see Writing Cache-Aware Scripts’.

      I’ve also seen a couple of references elsewhere to switching off Etag using: FileETag None.

      I have no dynamic content on my site and wonder whether your .htaccess code, amended to suit my application, would fully leverage browser caching, or do I need to do more than this with Etag or anything else.

      Thanks again,
      Andrew

  2. You should look at your website this like thing you have here is blocking me from viewing the txt on this site.

    Try having a fixed width

    • Hi John,

      Thank you for letting me know! The site has been cross-browser tested, but clearly there’s a browser version that’s been overlooked. Please let me know which browser, and browser version,you are using and I’ll ask our developers to fix it.

      Thanks,
      Tess

  3. Hi Tess,

    Thanks for the time and effort put into this tutorial. Very helpful and easy to follow!
    I did improve my score right away. Still a lot to do but this was a good start.
    Thankx again tigress!

    Paulo

    • Thank you so much Paulo for your positive feedback!

      If you have suggestions for other SEO related tutorials I should write, let me know.

      Good luck with your SEO’ing! :)

      Cheers,
      Tess

  4. hi tess

    thanks for the detailed information. I am on an AWS EC2 server – I hav eenabled the expires module and also followed the steps after restarting the apache. But the site still has leverage browser cachinh issues. I am not supposed to post the URL here .

    Any comments / help on this?
    Many thanks!
    Sathish

    • Hi Sathish,

      You say “the site still has leverage browser cachinh issues” – how have you confirmed this? If you can be more specific about what you mean by “issues” it’ll help me give you a better answer. :)

      Also, your host should be able to help you with any issues. All server setups are different, so your first step should be to check with them if they can help you pinpoint the problem and guide you towards a solution.

      Cheers,
      Tess

  5. thanks for the quick reply Tess
    I am on a linux box running on AWS.
    After doing the steps you have mentioned – still http://gtmetrix.com shows the leverage browser caching caching is 18, and says no expiry is set on the images.

    Thanks for the time – I will try to google more – let me know if You have some tips on this
    Many thanks!
    Sathish

  6. For some reason I still get a failing grade for leverage browser caching. I recompiled Apache/PHP with cache, memcache, file_cache and added the lines you provided above (without modification) to the .htaccess file in the public_html folder of my website. However, I still get an F for leverage browser caching.

    Am I missing a step?

    Oh yes, and Apache/PHP had mod_expires as a default.

    • Hi Nick,

      When I run a check for your site, I see some beautiful grades! You score A’s. :) You can see what I see here: http://gtmetrix.com/reports/labodanglais.com/EdBIdYoM

      The only thing you can add is for your ETags. Just add these 2 lines to your htaccess:

      # Turn off ETags
      FileETag None

      Well done!

      Cheers,
      Tess

  7. Do you have any idea if this will work WordPress websites?

    I added the rules to my .htaccess and it slapped me in the face with a “internal server error”. :)

    Could this be because of using WordPress or could it be the server config of my host?

    • Hi Erwin,

      This site is built on WordPress, so it’s not CMS related. In this case, an internal server error means you’ll have to do some checking of your server settings and htaccess code:

      1. To figure out what’s going on, and how to solve it, check your error logs. You might find useful info there (for example something like “ExpiresActive not allowed here”).

      2. Check with your hosting company if you have these Apache modules enabled and loaded (you need them to be): “mod_expires” and “mod_headers”

      3. Double-check that your code is correct. Typos are more common than we think. :) Take it step by step and add only one thing at a time to see if it’s the basics that fail, or if it’s a specific line.

      I hope the above helps? Let me know how it goes, and what the problem was.

      Thanks,
      Tess

    • FYI, if you get the “internal server error” it is because in the sample code provided in this post a couple of the quotation marks come in as curly quotes. Turn all the ” into ” and it will work.

      Tess, to save others a small headache, you may want to see if you can change those curly quotes into straight ones for others who may copy and paste.

      • Hi Danny,

        You’re absolutely right about the curly “. I’ve updated the code to be correct, so now it should be all good to copy/paste.

        Thanks for pointing it out. :)

        Cheers,
        Tess

  8. Good work Tess, and thanks.

    Quick question though: where exactly on our htaccess should we drop the above code for it to work properly?

    Thanks for your time !

    • Hi Eduardo,

      Thanks for the positive feedback. :)

      I personally drop any new code last in the .htaccess (but usually above my 301 redirects, if any).

      Make sure you save a copy of the original .htaccess so you can revert back in case it’s not working.

      Let me know how it goes.

      Cheers,
      Tess

  9. Helpful, thanks.

    What if I want to specify specific images only like a logo and set it for a year or two down the road. I tend to change things a bit and don’t want too many things set in the expires header.

    I would rather manually specify the ones I want. Is that easy enough?

    Thanks

    • Hi Jordan,

      I’d recommend you to specify expires header for your images for a month or two. There’s rarely a need for specifying it for 1-2 years.

      Remember that caching is set on the file names, so if you update any of your existing images you can still work around the browser caching by giving it a new name (and of course update the reference to it).

      It all comes down to how you work with your content – if you constantly update your existing images and use the same name for the updated version, then you might be better off not specifying caching for that format (.jpg/.png etc)

      I hope the above answers your question? Sometimes we just have to look at the bigger picture, and see how we can adapt things to our work-flow, or vice versa. :)

      Cheers,
      Tess

  10. Thanks Tess,

    It worked brilliantly. using your tip I boosted my site from 84 to 91 points. (Google Page Speed0)

    • That’s awesome news Guy! Thanks for letting me know. :)

  11. Thank you very much. My score jumped up a whole grade.

    I did change the expire dates to 1 week instead of 1 month to test.
    Does it make much of a difference if I change it to one month instead of one week. I sometimes go through update spells on my website so I wanted to give it shorter expiration to be safe.

    Thank you again! :)

    • Hi Deyson,

      Thanks for letting me know that it worked for your website. :)

      As far as I know, the recommended minimum expiry time is 7 days, so anything from there and up should be fine.

      Cheers,
      Tess

  12. Hello,

    Thanks for this tip!! Thou I have to work on the images still. But the result made me smile =)

    One question thou, after specifying the Expiry in .htaccess, is Last-Modified still require? If yes, how should it be added?

    Thanks again!!!

    • Hi Jina,

      Last-modified is a bit different than Expires, so you can use both. That said, I usually make it easy for myself and just add Etag instead of Last-modified, which is similar.

      See my reply to Nick for more info about how to add Etag: http://fortheloveofseo.com/blog/performance/leverage-browser-caching-how-to-add-expires-headers/#comment-287

      I hope this helps? :)

      Cheers,
      Tess

      • Hello Tess,

        It did help! Thanks a lot =) But one question thou, what’s the difference between implementing mod_headers and mod_expires? I’ve seen some implement it this way. Been doing some reading about it, but couldn’t quite grasp the difference exactly.

        # Expire headers

        ExpiresActive On
        ExpiresDefault “access plus 1 seconds”
        ExpiresByType image/x-icon “access plus 2592000 seconds”
        ExpiresByType image/jpeg “access plus 2592000 seconds”
        ExpiresByType image/png “access plus 2592000 seconds”
        ExpiresByType image/gif “access plus 2592000 seconds”
        ExpiresByType application/x-shockwave-flash “access plus 2592000 seconds”
        ExpiresByType text/css “access plus 604800 seconds”
        ExpiresByType text/javascript “access plus 216000 seconds”
        ExpiresByType application/javascript “access plus 216000 seconds”
        ExpiresByType application/x-javascript “access plus 216000 seconds”
        ExpiresByType text/html “access plus 600 seconds”
        ExpiresByType application/xhtml+xml “access plus 600 seconds”

        # Cache-Control Headers

        #month

        Header set Cache-Control “max-age=2592000, public”

        #week

        Header set Cache-Control “max-age=604800, public”

        #day

        Header set Cache-Control “max-age=43200, private, must-revalidate”

        Thanks and Regards,
        Jina

        • Hi Jina,

          To put it very simple: I prefer to use Expires over Cache-Control since it’s more widely supported, and it’s also what Google recommends.

          This is a good resource to read: https://developers.google.com/speed/docs/best-practices/caching#LeverageBrowserCaching

          “It is important to specify one of Expires or Cache-Control max-age, and one of Last-Modified or ETag, for all cacheable resources.

          It is redundant to specify both Expires and Cache-Control: max-age, or to specify both Last-Modified and ETag.”

          I hope this answer helps? :)

          Love & light,
          Tess

      • Sorry for the long code of copy and paste.

        Here’s the link on it,
        http://stackoverflow.com/questions/8513791/htaccess-caching-not-working

        Thanks again,
        Jina

  13. Hi Tess – If someone visits your site and clicks a page for the first time is there any speed gain loading the page using expires headers. PS love Tigeress description :-)

    • Hi Mark,

      Browser caching is only beneficial when the visitor comes back to your site, not for first time visits. So you need to make sure you’re already running your site on a fast server, and that you’ve optimized your code etc as much as possible. Every little bit counts.

      Have a look at my checklist for an SEO audit, and work your way through it to improve your overall SEO even for first time visitors: http://fortheloveofseo.com/seo-services/technical-seo-audit-service/technical-seo-audit-checklist/ (it’s only a checklist, so use it as a base for further research on each topic)

      Love & iight,
      Tess

  14. I added these to my .htacces but I still get 7 components needing expiring.

    http://gtmetrix.com/reports/frankaguilar.com/t3jxK878

    • Hi Frank,

      Yes unfortunately all those components are external, i.e. they are not yours. As far as I know, there is no (easy) way to set expires headers / browser caching on any of those items. :(

      Love & light,
      Tess

      • Wow, thank you for your quick reply.
        That’s too bad to hear. It did however clear up all my internal files.
        Thank you for this article, it really cleared up a lot!

        Subscribed to your RSS!

        • Every bit counts, so the work you’ve done based on this tutorial is a big step in the right direction. :) And your result on gtmetrix looked very good in general – well done!

  15. Still confused. I still try to do something with kdgroup.net/ja/telineiv/ and still does not success. Google page speed still give me 47/100 score. Is there any way to do this thing with header tag in php instead of .htaccess file?

    • It seems you have implemented browser caching perfectly already. See this test result: http://gtmetrix.com/reports/kdgroup.net/lZ0KYZjg

      There you will see that you have other issues you must sort out for your website. Go through the list and improve on each item one by one. Run a new test of your site there after each improvement to check if it’s working.

      I hope this helps? :)

      Cheers,
      Tess

  16. Thank you Tess! – worked like a charm.

    I am just getting started with my limousine reservation business (after 7 years of working every position in the limousine reservation field) and will surely be returning to your page for advice and tips.

    By chance do you work on contract? I may be in crucial need of a full-time, trustworthy, dedicated SEO person…

    Again, thank you!

    • Hi Andre,

      Awesome that this blog post helped you! Keep on going, one step at a time, and you’ll take your new website/business to great heights! :)

      I do work on contract: http://fortheloveofseo.com/seo-services/

      When you’re ready to get going with your SEO and emarketing, drop me a message via my contact form and we’ll talk: http://fortheloveofseo.com/contact/ (full-time contract is not an option, but long-term commitment and dedication is) ;)

      Love & light,
      Tess

  17. Thanks for this article. Just had a quick read through and after doing a benchmark on my website with a couple of others, it’s given me a few pointers to address and possible areas of improvements that I had left out of my site.

    • Hi Stephen,

      I’m happy this article could contribute with a few pointers. :)

      Cheers,
      Tess

  18. I want to know , if i add

    # Default directive
    ExpiresDefault “access plus 1 month”

    So it default cache to html page ? or only cache images,gif,jpg ?

    And i want to know how to cache html output and how to set expires date ?
    This post is very useful for me and improve all of my web sites page speed.

    Thank you so much.

    • Hi Linn,

      The ExpiresDefault caches pretty much everything, so I always include it just to cover the basics. Then adjust for the specific file types depending on how often I change them. j

      In general, html files load quickly so you don’t have to set any specific expires dates. Rather just make sure the Default is set to a reasonable time frame. If you update the content of your web pages often, then you might want to drop the Default down to 1 week instead of 1 month.

      I hope the above helps?

      Cheers,
      Tess

  19. And I also want to know .. some people write –

    ExpiresByType image/gif “access plus 1 years” .. they add ‘yearS’
    ExpiresByType image/gif “access 1 year – they remove ‘PLUS’ and ‘S’

    what different between ‘PLUS’ .

    Thank you .

    • Hi again Linn,

      I always do things according to official guidelines, and in the Apache guide for mod_expires, they recommend using plus: http://httpd.apache.org/docs/2.0/mod/mod_expires.html

      I’ve seen discussions about using plus or not, and some people having problems when excluding it. I can not say that it would be the reason for their problems, but just to be safe I recommend you to use plus.

      Regarding using year or years, same thing there: follow the guidelines in the above document. Apache recommends using plural (years, etc).

      Best thing to do is to implement it in your htaccess file and run your site through gtmetrix. Remove the plus, and set the time to singular (year), and run the test again. Do you still get the same result?

      Cheers,
      Tess

  20. Hi Tess – excellent post!

    I’ve followed your post as closely as I can figure as well as talking through the issues with my web host (GoDaddy) and reading many articles on their site about how they handle mod_expires and yet still, I can’t manage to fix the “Leverage browser caching” recommendation on GT Metrix (http://gtmetrix.com/reports/indie-farms.com/Q3aSRTpS).

    My .htaccess file is in my site’s root folder and its contents are:

    # Activate mod_expires for this directory
    ExpiresActive on

    # Default directive
    ExpiresDefault “access plus 1 month”

    # My favicon
    ExpiresByType image/x-icon “access plus 1 year”

    # Images
    ExpiresByType image/gif “access plus 1 month”
    ExpiresByType image/png “access plus 1 month”
    ExpiresByType image/jpg “access plus 1 month”
    ExpiresByType image/jpeg “access plus 1 month”

    #css
    ExpiresByType text/css “access plus 1 month”

    # Javascript
    ExpiresByType application/javascript “access plus 1 year”

    What am I missing?

    • Hi Jessica,

      It looks like your ” characters are not right. If you look closely, they’re a little “curly” compared to the straight ones in this blog post.

      Copy the relevant lines from this blog post and paste it in your htaccess – that should sort it out.

      Let me know if that helped. :)

      Cheers,
      Tess

      • Hi Tess,

        I went back and pasted in the exact copy from your post (I’ll repaste it below so that you can see the ” and other details), but I’m still not seeing any changes to my GTMetrix score.

        I read somewhere that GoDaddy’s servers can take 2-3 hours to implement changes like this – is that possible?

        What else might I need to do to get this working?

        Thanks so much for your help – super appreciated!

        Jessica

        # Enable expirations
        ExpiresActive On

        # Default directive
        ExpiresDefault “access plus 1 month”

        # My favicon
        ExpiresByType image/x-icon “access plus 1 year”

        # Images
        ExpiresByType image/gif “access plus 1 month”
        ExpiresByType image/png “access plus 1 month”
        ExpiresByType image/jpg “access plus 1 month”
        ExpiresByType image/jpeg “access plus 1 month”

        # CSS
        ExpiresByType text/css “access 1 month”

        # Javascript
        ExpiresByType application/javascript “access plus 1 year”

        • OK, you might be looking at my reply and thinking, “Well, those quotes are still curly.”, as that’s what I’m now wondering.

          They appear straight in my htaccess file and they were straight when I replied to your comment, but they’re showing as curly even though I copied/pasted them straight from your post.

          Is this an issue with how I’m saving my file, the methods I used to create an htaccess file (via Dreamweaver) or something else?

          Thanks again for your help!

        • Hi Jessica,

          Have you checked with your hosting company about this? For example, make sure they actually allow you to add Expires headers: “your server must be Apache (and requires the module mod_expires)”

          Because all server setups are different, my best advice at this point is that you contact your hosting company and ask them what might be wrong. They will be able to give you a good answer based on your server setup, and they might even have to enable something for this to work. :)

          Please let me know how it goes.

          Cheers,
          Tess

          • Yeah, I actually called after I read this article on their help site about htaccess. They allow it and give instructions for how to enable mod_expires.

            I’ll have to call again and see what they have to say. Thanks so much for walking me through this, though. If I uncover the mystery, I’ll be sure to post it for others to use here.

            Cheers!

          • And the mystery is solved!

            For those using GoDaddy as their webhost, you’ll have to choose the Linux based server rather than the Windows based server for your account. It costs the same.

            To choose this in your account, login to your account at GoDaddy >My Account>Web Hosting>Click “Options” next to the domain you want to use .htaccess on>Choose the “Customize” tab>Under “Plan” choose “Hosting-Web-Economy-Linux-US Region” (or whatever your current plant’s Linux option is).

            It will take up to 72 hours to update your server (mine took about 3 hours) and you’ll have a new IP address that you’ll have to update in your FTP client. You’ll also probably have to delete your htaccess file from GoDaddy’s FTP side and FTP it in again from your side, but once you’ve done ALL of that, your htaccess file will work immediately. Don’t forget to include GoDaddy’s required code that will activate htaccess for your use:

            # Activate mod_expires for this directory
            ExpiresActive on

            Check it: http://gtmetrix.com/reports/indie-farms.com/uAcj8P0Q

            Thanks for the help, Tess!

            FYI: Don’t be deterred by GoDaddy’s people telling you that your htaccess code is wrong. Mine was fine, but they got all, “Um, I’ve never seen a htaccess file like this before. That’s your problem. You should rewrite it.” and such. Total BS.

            My final usable htaccess file is:

            # Activate mod_expires for this directory
            ExpiresActive on

            # Default directive
            ExpiresDefault “access plus 1 month”

            # My favicon
            ExpiresByType image/x-icon “access plus 1 year”

            # Images
            ExpiresByType image/gif “access plus 1 month”
            ExpiresByType image/png “access plus 1 month”
            ExpiresByType image/jpg “access plus 1 month”
            ExpiresByType image/jpeg “access plus 1 month”

            # CSS
            ExpiresByType text/css “access 1 month”

            # Javascript
            ExpiresByType application/javascript “access plus 1 year”

          • That is so awesome Jessica! So happy for you, and so grateful that you came back here with the comprehensive explanation and solution!! Thank you so much!! :)

  21. Hi Tess,
    Thanks for the article.
    Would I be correct to use the following

    # Flash
    ExpiresByType application/x-shockwave-flash “access plus 1 year”

    to cater for swf files?

    Kind regards

    • Hi Robert,

      Yes, that looks right. Did you try it, and did it work?

      Cheers,
      Tess

  22. Absolutely! Cheers!

  23. Thanks a lot, this article is very well described and I managed to make it work and improved the rate of my website !
    thanks you : )

    • Hi Amandine,

      That’s awesome! Thank you for taking the time to comment – I love when my info helps others. :)

      Cheers,
      Tess

  24. Hi Tess

    This article really improved the performance of my WordPress site:-)
    When I remove the code(in this article) from the .htaccess file and replace it with the old one, my site goes extremly slow. The reason I did this is because my site is still under construction.
    Do you have an idea why my site is slow?

    Defalut code in the .htaccess file:

    # BEGIN WordPress

    RewriteEngine On
    RewriteBase /
    RewriteRule ^index.php$ – [L]
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule . /index.php [L]

    # END WordPress

    • Hi Øyvind,

      There can be many reasons why your site is slow. One of the best ways to get your basics right is to use http://gtmetrix.com and improve all the scores there.

      If your site is still slow after that…you might have to look into the server you’re using, and also check if it’s actually slow for your visitors…or only slow for you.

      Best of luck! Let me know how it goes, and if you find a particular reason for your site being slow – it’s always interesting to learn what helped for others.

      Cheers,
      Tess

  25. Hi…… Tess, Thank you, Thank you and Thank you for “n” times, because there is a reason to say “n” times thanks. At first time I checked my website at Google PageSpeed and I found 57 and I did the best to my knowledge and I bought it to 74 and after that I found this issue “Leverage browser caching” and it is out of my knowledge and I sought help from my website hosting support but they are also informed that it is out of their work area. Next decided to search in Google and I found your post at first minute and followed as exactly what you suggested and the page speed is jumped to 93 and really wonderful. Really a great solution for newbie’s like me. Once again thank you Tess, have a nice day.

    • Thank you so much Pshaik! It warms my heart to hear that my suggestions are easy to understand and implement – it’s the main reason I put so much work into this article.

      I simply could not find a decent and easy-to-understand browser caching tutorial for newbies, which is odd because it’s such a hot topic. Well, now there is a good one on the internet, so my work here is done. ;)

      Love & light,
      Tess

  26. I spoke too soon after the improved ratings the code brought my site down with a 500 error. Removed the code and it is back up again. Do you have any ideas why this might have happened. The host already has expires_mod on so perhaps I don’t need the first part of the code?
    Thank you for your time

  27. I have got to take my hat off to you Tess I have been running a joomla 2.5 site with kunena forum and kept on getting leverage browser caching deadly scores on Google and pingdom it was at 4. I added various .htaccess codes from various sites and nothing really made a difference max 2 points until I added your code and wow I now have a google page score of 99 whereas before it was 56. This is incredible and I put this down to all that warm sun you get in SA plus the good food etc. Massive thank you for this and I just need to check now if your code affects members updating their avatars and also code on the kunena forum if so it may just need a little fine tuning. David

    • Awwhhh, David, you make me blush. ;) Thank you so much for your positive feedback!

      Regarding the Kunena avatars, you might be able to get around the caching of them by putting the avatars in a separate folder, and set the folder to no-cache. It’s a bit of a different beast to do, but it should be possible as far as I know.

      Good luck, and please let me know what solution you implement – I’d love to find out!

      Cheers,
      Tess

  28. Just thought I’d share my thanks for such a brilliantly executed post.

    Thanks for sharing.

    • Thank you Aaron! :)

  29. thanks really helping me,, i used it like this

    ExpiresActive On
    ExpiresDefault “access plus 1 month”

    ExpiresActive On

    FileETag None

    ExpiresByType image/x-icon “access plus 1 year”
    ExpiresByType image/gif “access plus 1 month”
    ExpiresByType image/png “access plus 1 month”
    ExpiresByType image/jpg “access plus 1 month”
    ExpiresByType image/jpeg “access plus 1 month”
    ExpiresByType text/css “access 1 month”
    ExpiresByType application/javascript “access plus 1 year”

    and got rank A on pagespeed gogle and gtmetrix

    • That’s awesome Jimy! :) Thanks for sharing your success here in the comments – I love knowing how my blog helps others succeed.

      Cheers,
      Tess

  30. I just add this to my .htaccess and page speed grade got doubled 23% to 42%..still need few adjusting but it helped alot..great stuff..thanks

    here is what I added for my client’s .htaccess

    # Enable expirations
    ExpiresActive On

    # Default directive
    ExpiresDefault “access plus 1 month”

    # Images
    ExpiresByType img/gif “access plus 1 month”
    ExpiresByType img/png “access plus 1 month”
    ExpiresByType img/jpg “access plus 1 month”
    ExpiresByType img/jpeg “access plus 1 month”

    • Fantastic! :) Thanks for taking the time to share this with me here in the comments, it’s so great to know how people use the info here on the blog.

      Cheers,
      Tess

  31. Hi Tess,

    Fantastic results I got!!! Thanks… Made all changes exactly as per your post. It was all working perfectly…

    BUT, I just upgraded to Joomla 2.5.9 and got a 500 server error. I had to remove all your changes from the .htaccess files, except for the following:

    # Enable expirations
    ExpiresActive On

    Now the site is back, but I really would like your suggested enhancements back….

    Can you please assist?

    Thanks,

    Andrew

    • Hi Andrew,

      I’m happy to hear that it all worked so well! :)

      My best advice is that you add back the improvements, item by item. When one item is added, save your .htaccess file, and reload the front-end.

      That way you can see exactly what might not be working.

      Let me know how it goes.

      Cheers,
      Tess

      • Hi Tess, as I mentioned before… I removed all your changes from the .htaccess files, except for the following:

        # Enable expirations
        ExpiresActive On

        As soon as I activated any of the other lines it gave me a 500 error.

      • this is my current .htaccess entries:

        # Enable expirations
        ExpiresActive On

        # Default directive
        # ExpiresDefault ”access plus 1 month”

        # My favicon
        # ExpiresByType image/x-icon ”access plus 1 year”

        # Images
        # ExpiresByType image/gif ”access plus 1 month”
        # ExpiresByType image/png ”access plus 1 month”
        # ExpiresByType image/jpg ”access plus 1 month”
        # ExpiresByType image/jpeg ”access plus 1 month”

        # CSS
        # ExpiresByType text/css ”access 1 month”

        # Javascript
        # ExpiresByType application/javascript ”access plus 1 year”

    • Hi Andrew,

      First, take a look at my replies to the following comments (maybe one of them will solve your error)

      http://fortheloveofseo.com/blog/performance/leverage-browser-caching-how-to-add-expires-headers/#comment-79

      http://fortheloveofseo.com/blog/performance/leverage-browser-caching-how-to-add-expires-headers/#comment-92

      Please let me know if that helps. :)

      Cheers,
      Tess

      • Hi Tess,

        Strange, it all looks fine.

        My concern though: It was all working perfectly prior to us upgrading to Joomla 2.5.9 from 2.5.8.

        It worked 100% on 2.5.8.

        Do you have an explanation for that.

        Thanks,

        Andrew

        • Hi Tess,

          Further to my last response. I found the following errors in the Apache log:

          [Mon Feb 11 19:17:59.631287 2013] [core:error] [pid 90481] AH00124: Request exceeded the limit of 10 internal redirects due to probable configuration error. Use ‘LimitInternalRecursion’ to increase the limit if necessary. Use ‘LogLevel debug’ to get a backtrace.

          [Mon Feb 11 20:08:41.894944 2013] [core:alert] [pid 97657] /public_html/.htaccess: ExpiresDefault takes one argument, an expiry date code, referer: http://www.my-domain.com/

          Also resolved the issue…

          It was the tags, these ” instead of these “.

          It was just copied and pasted from your blog above.

          Very strange, but maybe you can help me with the other error:

          [Mon Feb 11 19:17:59.631287 2013] [core:error] [pid 90481] AH00124: Request exceeded the limit of 10 internal redirects due to probable configuration error. Use ‘LimitInternalRecursion’ to increase the limit if necessary. Use ‘LogLevel debug’ to get a backtrace.

          Lastly, should there also be header entries in the .htaccess file:

          # any Header directives go here

          Thanks,

          Andrew

    • Hi again Andrew,

      Regarding your error “Request exceeded the limit of 10 internal redirects due to probable configuration error. Use ‘LimitInternalRecursion’ to increase the limit if necessary”

      Start with this post to see if it might be the problem:

      http://www.egometry.com/gruedorf/request-exceeded-the-limit-of-10-internal-redirects-due-to-probable-configuration-error/ (an .htaccess file in a folder higher in the hierarchy)

      If not, contact your web host to see if they can help you. There seems to be an issue related to something else than the expires headers.

      Let me know how it goes, and what the solution was.

      Thanks,
      Tess

  32. Hi Tess, What a great article. It was simple to understand and easy to follow and implement. I’ve looked up other information on site speed fixes in the pass and found them to be confusing or overwhelming at time cause they got too technical.

    It would be nice to have a resource like this that walks you through each page speed result. Do you have other posts on this topic, or do you know of a good resource that already exists?

    The best posts are the ones that say, “This is the problem, This is why, and here’s how you correct it.” Your post did exactly that!

    • Thanks for your nice feedback! Always appreciated. :)

      I usually just follow Google’s own recommendations, or search the web for details. That said, I wrote this post because I couldn’t find any understandable info out there for this particular topic.

      Please throw me a few topics you haven’t found good/understandable info about, and I’ll either write a kick-ass blog post about it or recommend existing great tutorials. :)

      Thanks,
      Tess

  33. Ah! Thank you! I went from 87 to 91 when I ran the speed test again. I really appreciate your post!

    • That’s great Christy! :)

  34. Hey there!

    thanks for the nice article. I implemented the code on my site and Pagespeedranking (Google) went up from 70 to 75. ;-)

    But Yslow didn’t change. It sill says that browser caching is not active.

    Greetings Martin

  35. Tess thank you so much for a simple explanation, & easy to implement method of dealing with browser caching. I have been putting this of for 15 months + cant believe how easy it was Thanks :-)

  36. Hi, Thank you for great Post.

    Well, guys, none of the above given solution worked for me, even many of Google Results helped me.

    So, here is a Quick fix to leverage the Cache.
    Copy below code as it is without touching anything and paste directly into your .htaccess file. It will drastically improve your Google Page Speed test.

    Code:
    #####################################################
    # CONFIGURE media caching
    #
    Header unset ETag
    FileETag None

    Header unset Last-Modified
    Header set Expires “Fri, 21 Dec 2012 00:00:00 GMT”
    Header set Cache-Control “public, no-transform”

    #
    #####################################################

    Hope, it helps.

  37. Managed to get 99/100 for my domain, thanks a lot Tess!

  38. This is the first article I found that explains clearly what expires headers are about.

    Thanks a lot.

  39. Wow . This is the best article on leveraging browser cache i come across. i have learned in-depth information on this topic. thanks for this article. you’ve made my day

  40. Hi, Tess

    I have a WordPress site (sub-domain) that already has this .htacess code:

    # BEGIN WordPress
    RewriteEngine On
    RewriteBase /
    RewriteRule ^index\.php$ – [L]
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule . /index.php [L]

    # END WordPress

    Where do I put the code you wrote relative to the code that is already there, please? Do I simply put it after # END WordPress?

    Thanks and namasté,
    Doug

  41. HI Tess,

    I tried this on my website, http://www.classifiedadland.com and am still trying to decide if it has made a difference. I also use GTmetrix (what an awesome tool) and am getting varying load times with and without the htaccess changes.

    What might have some affect on it is I’m also using Wp Super Cache, Wp Widget Cache and Wp Image Smush, which already speed things up quite a bit.

    But my site didn’t have a site wide htaccess file until I created one for this purpose. It has one in the classified module, which I have not played with. Should I be modifying that one, as well? I don’t believe it affects the front page load time, which is why I haven’t changed it.

    Thanks for a really great article. This WILL help other sites I’m running.

  42. Hi Tess

    This is great it really help my project to speed up my boss love the speed results of his website :D

    I have a question about versioning the javascript and css file. What if i use an internal javascript and css do i have no worry in versioning??

    THANKS :D

  43. So happy to have discovered this post! Thank you for the clear and detailed instructions for improving browser caching.

    Before: Page speed 67% YSlow Grade: 77%
    After Page speed 76% YSlow Grade: 84%

    Now I need to check out GZIP to compress CSS and JavaScript. Looks like I’ll be adding more code to my htaccess file…

    Tess, THANK YOU for providing excellent instructions!

  44. Thanks a lot Tess for such a brilliant post.
    I added these to my .htacces but I still get 6 components needing expiring.

    http://gtmetrix.com/reports/etypeservices.net/AwhFwUG5

    and in YSLOW Add Expires headers is still lie in F Grade . How to improve this percentage in YSLOW.

  45. Really helpful article.

    BTW in the complete example given, you have
    ExpiresByType text/css “access 1 month”
    for CSS instead of
    ExpiresByType text/css “access plus 1 month”

    Is that a typo? Or is there something special about CSS?

    TIA

    Alan

  46. Hi Tess
    I am very new to this and don’t know where in the htaccess.txt file to add your info or do I add this in .htaccess My page is very slow arond 5 sec to load
    maybe i can send you the file?

    Thanks
    Jeanette

  47. Hi Tess. This is a very good article. It easy to understand. Even its already 2 years old. LOL. Btw, can you explain or make a tutorial about gzip thingy? Using GTMetrix tool the comments sound like “Enable gzip compression” (pagespeed) and “Compress components with gzip” (YSlow).

    Thanks in advance. :)

  48. Thanks Tess, very well explained and clear as well.
    My website speed went from 85% to 92% after making the changes to .htaccess you suggested. Really appreciated…Mark

  49. Really clear explanation and worked perfectly. Thanks!

Trackbacks/Pingbacks

  1. Quora - How do I effectively implement header expiry / caching for Wordpress? - "I would never recommend you to cache things forever, that just doesn't make sense. Rather list the items of your …
  2. Disable CDN Consensus? - Ecommerce Forums - [...] browser caching is totally different from using a CDN. Here is a pretty good explanation: http://fortheloveofseo.com/blog/perf...pires-headers/ …
  3. 20 Quick Tips to Optimize Page Load Time | Digital Marketing Blog | WooRank Blog: Digital Marketing tips for Small Business - [...] can use Expires headers for static components of the site and Cache-Control headers for dynamic ones. Using these headers …

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>