Adding Stale-While-Revalidate And Stale-If-Error

Posted at


This little trick speeds up browsing for repeating-customers/readers of any website!
(Also improve background data-transfer and streaming!! and makes your browser faster!)


You're probably familiar with Cache-Control header, setting max-age (seconds) for each response-resources (image, pages, scripts),


Usually similar to Expires header which specify a target date instead of a delta (but the same).

by default, when a resource expires, it does so while the user is requesting the page (again),
the resource is fetched (fresh) but makes the user wait while the page is downloaded at that point in-time,
all to prevent stale resource.

The stale-while-revalidate addition to the Cache-Control header allows the current view to STILL be the old/stale one, but at the same time trigger the background fetching the newer resources,
so the user may get the latest one next time.







You don't have to use Cache-Control with the max-age if you don't like to,
you can just use the flags stale-while-revalidate and stale-if-error,
by specifying (on Apache) Header append "Cache-Control" "stale-while-revalidate=86400" and Header append "Cache-Control" "stale-if-error=259200",
which would not remove the old (if any) header.

I now serve every-resource using the additional flags, which helps to make the internet fast for everyone :)

Here is a part (snippet) of the .htaccess I'm using with all of eladkarako.com resources (as of now..)

###  #####################
### ## E X P I R E S ##
### #####################
### # ----------------------------------------------------------------------
### # Expires headers (for better cache control)
### # ----------------------------------------------------------------------
###
### # These are pretty far-future expires headers.
### # They assume you control versioning with cachebusting query params like
### # <script src="application.js?20100608">
### # Additionally, consider that outdated proxies may miscache
### # www.stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring/
###
### # If you don't use filenames to version, lower the CSS and JS to something like
### # "access plus 1 week" or so.
###
<IfModule mod_expires.c>
ExpiresActive on
      #Perhaps better to whitelist expires rules? Perhaps.
ExpiresDefault "access plus 1 month"
      #cache.appcache needs re-requests in FF 3.6 (thanks Remy ~Introducing HTML5)
ExpiresByType "text/cache-manifest" "access plus 0 seconds"
      #Your document html
ExpiresByType "text/html" "access plus 2 minutes"
      #Data
ExpiresByType "text/xml" "access plus 2 minutes"
ExpiresByType "application/xml" "access plus 2 minutes"
ExpiresByType "application/json" "access plus 2 minutes"
      #Feed
ExpiresByType "application/rss+xml" "access plus 2 minutes"
ExpiresByType "application/atom+xml" "access plus 2 minutes"
      #vCard
ExpiresByType "text/x-vcard" "access plus 2 minutes"
      #Favicon (cannot be renamed)
ExpiresByType "image/x-icon" "access plus 1 year"
      #Media: images, video, audio
ExpiresByType "image/webp" "access plus 2 months"
ExpiresByType "image/gif" "access plus 2 months"
ExpiresByType "image/png" "access plus 2 months"
ExpiresByType "image/jpg" "access plus 2 months"
ExpiresByType "image/jpeg" "access plus 2 months"
ExpiresByType "video/ogg" "access plus 2 months"
ExpiresByType "audio/ogg" "access plus 2 months"
ExpiresByType "video/mp4" "access plus 2 months"
ExpiresByType "video/webm" "access plus 2 months"
ExpiresByType "audio/mp4" "access plus 2 months"
      #HTC files (css3pie)
ExpiresByType "text/x-component" "access plus 2 months"
      #binary (exe and other)
ExpiresByType "application/octet-stream" "access plus 1 year"
      #chrome extension
ExpiresByType "application/x-chrome-extension" "access plus 1 year"
      #firefox plugin
ExpiresByType "application/x-xpinstall" "access plus 1 year"
      #Webfonts
ExpiresByType "application/x-font-ttf" "access plus 1 year"
ExpiresByType "font/truetype" "access plus 1 year"
ExpiresByType "font/opentype" "access plus 1 year"
ExpiresByType "application/x-font-woff" "access plus 1 year"
ExpiresByType "application/font-woff" "access plus 1 year"
ExpiresByType "image/svg+xml" "access plus 1 year"
ExpiresByType "application/vnd.ms-fontobject" "access plus 1 year"

      #CSS and JavaScript
ExpiresByType "text/css" "access plus 1 year"
ExpiresByType "text/javascript" "access plus 1 year"
ExpiresByType "application/javascript" "access plus 1 year"

      # ----------------------------------------------------------------------
      # ETag removal
      # ----------------------------------------------------------------------
      # Since we're sending far-future expires, we don't need ETags for static content. developer.yahoo.com/performance/rules.html#etags
FileETag None
      # FileETag None is not enough for every server.
<IfModule mod_headers.c>
Header unset ETag
</IfModule>

      # ----------------------------------------------------------------------
      # Cache-Control (with only stale flags, no need for max-age.. for now..)
      # ----------------------------------------------------------------------
<IfModule mod_headers.c>
Header append Cache-Control "stale-while-revalidate=86400,stale-if-error=259200"
</IfModule>
</IfModule>


p.s. you can use one append command (with comma as separator) or two append commands.
- I use one, since I can control the space added after the comma (I prefer the HTTP-header's values to do not include whitespace-characters, as much as possible...:\\)