This is about a web app that serves images. Since the same request will always return the same image, I want the accessing browsers to cache the images as aggressively as po
You could add an ETag
representation for each image and then compare it to the If-None-Match
header on inbound requests (see "Why isn’t my custom delivered image caching in the browser?"). This is redundant when using the preferred Last-Modified
header and it's just another way to say 304 anyway. (I think GAE does this automatically for static files, not sure though.)
Gravatar sets very old Last-Modified
dates -- the default seems to be "Wed, 11 Jan 1984 08:00:00 GMT". The 5-minute expiration causes browsers to check for updated images frequently. In other words, I think they're inviting 304s, not trying to convince browsers to just use the local cache. Their headers look like this:
Date: Sat, 20 Mar 2010 07:52:43 GMT
Last-Modified: Wed, 11 Jan 1984 08:00:00 GMT
Expires: Sat, 20 Mar 2010 07:57:43 GMT
Cache-Control: max-age=300
The big difference is the expiration time -- you want two days, they want five minutes. So if you want browsers to just use the cached image for 48 hours, do what you're doing, only set Cache-Control: max-age=172800
(86400 is 24 hours).
I don't know that it'll help beyond what solutions others have offered, but you could use the HTML5 offline web apps facilities to more explicitly ask the browser to store a local copy.
Try .htaccess like
<ifmodule mod_gzip.c>
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.*
</ifmodule>
<ifmodule mod_deflate.c>
AddType application/x-compress .Z
AddType application/x-gzip .gz .tgz
AddType application/x-httpd-php .php
AddType application/x-httpd-php .php3
AddOutputFilterByType DEFLATE text/html
AddOutputFilterByType DEFLATE text/plain
AddOutputFilterByType DEFLATE text/xml
AddOutputFilterByType DEFLATE application/x-httpd-php
AddOutputFilterByType DEFLATE application/x-javascript
</ifmodule>
<ifmodule mod_expires.c>
ExpiresActive On
ExpiresDefault "access plus 1 seconds"
ExpiresByType text/html "access plus 1 seconds"
ExpiresByType image/gif "access plus 2592000 seconds"
ExpiresByType image/jpeg "access plus 2592000 seconds"
ExpiresByType image/png "access plus 2592000 seconds"
ExpiresByType text/css "access plus 604800 seconds"
ExpiresByType text/javascript "access plus 216000 seconds"
ExpiresByType application/x-javascript "access plus 216000 seconds"
</ifmodule>
<ifmodule mod_headers.c>
<filesMatch "\\.(ico|pdf|flv|jpg|jpeg|png|gif|swf)$">
Header set Cache-Control "max-age=2592000, public"
</filesmatch>
<filesMatch "\\.(css)$">
Header set Cache-Control "max-age=604800, public"
</filesmatch>
<filesMatch "\\.(js)$">
Header set Cache-Control "max-age=216000, private"
</filesmatch>
<filesMatch "\\.(xml|txt)$">
Header set Cache-Control "max-age=216000, public, must-revalidate"
</filesmatch>
<filesMatch "\\.(html|htm|php)$">
Header set Cache-Control "max-age=1, private, must-revalidate"
</filesmatch>
</ifmodule>
There is a very important value on cache header that you have not mentioned here:
"post-check=900, pre-check=3600"
Read this article about this topic (and search for more):
http://www.rdlt.com/cache-control-post-check-pre-check.html
You can do better. 304s are still a HTTP request/response. Though the image is not downloaded again, the latency can be killing.
If you can include a version identifier in your image names, you can set the max-age to 2 years. That way, you prevent 304s. If the image ever changes, you update the version identifier thereby changing the file name. This ensures that the browser will issue a fresh request.
It needs some changes to your project structure. The version identifier can be the SVN revision number when the image was last updated, and can be auto-generated at build time. You'd also need to update the html, so if you have a logical mapping between image name and image path, your job would be easier.
Images are rarely updated, so you could also follow a manual approach if you can't automate what I described above. The trick is to only add new images, never modify them.
You may be interested in checking out the following Google Code article:
In a nutshell, all modern browsers should be able to cache your images appropriately as instructed, with those HTTP headers.