Acunote is online project management and Scrum software. Acunote is fast and easy to use. It shows actual progress, not just wishful thinking. Click here to learn more.
« Back to posts

Why You Should Use 'Cache-Control: public' for Caching to Work with SSL

We've been using long Expires and Cache-Control: max-age headers to mark our javascripts and css as browser-cacheable for quite some time. This way the browser only does full GETs on these once, and avoids conditional GETs except on page refresh. This only requires a few lines of configuration in Rails (asset timestamps and asset packaging) and nginx and generally works well.

Except when it does not. We've had some bug reports that in some cases under SSL these files were not cached at all. Meaning, just going to a different page in our application would make browser do an unconditional GET to refetch css and javascript. Naturally, this would hurt the performance.

Now, SSL in itself makes things worse. Firefox only caches SSL content in memory not on disk, meaning if the user closes the browser the cache goes away. But the reported behavior was worse than that -- no caching at all.

It took us a while, but we did manage to chase it down. It seems that Firefox divides SSL content into page marked with Cache-Control: public, which have priority for caching, and the rest. When you open enough tabs, say 50+, with enough rich pages in Firefox, its in-memory cache fills up and refuses to cache non-public SSL content. We haven't had time to fully chase it down in the FF codebase, but what's likely to happen is that FF actually does put in into cache, then realizes that cache is full, looks for pages to evict, and right away evicts the newly added content. Since SSL content is not cached on hard disk, once the memory cache is full, the non-pubic SSL files are not cached at all. (Note that's just a hypothesis, we'd be most curious to know what actually happens and why.)

The server-side workaround is to add Cache-Control: public to all your SSL content you want cached. We do:

    # nginx configuration for static content
location /static { expires 1y; add_header Cache-Control public; }

This bug is present in Firefox 2 and 3, but as a user you can configure them to work around the problem by setting browser.cache.memory.capacity to increase the size of memory cache or using browser.cache.disk_cache_ ssl to enable disk cache for SSL pages. The default 24M memory cache size for systems with 2G of RAM is really small, especially with FF3 being able to handle hundreds of tabs well. Note that the disk_cache option works only on Firefox 3 because of some bugs in earlier browser versions.

Small remark for Acunote users - you don't have to do anything about this. We enabled Cache-Control: public and your Firefox will cache javascript and css files no matter how many tabs you have.

Read comments on this post on Hacker News