So you've developed an awesome single page application and now you're ready to launch it. Sweet, good job! But hold on there buddy, before you go pulling the trigger and Tweeting your pun-tastic URL take a second to make sure you have your caching strategy in place. Otherwise you may find yourself in a world of hurt the next time you push an update.
I speak from personal experience, and here's what I learned...
Before I dive in, let me precursor this with: I'm not going to get into the nitty gritty on how to set everything up. Each dev environment is different and requires different steps. What I want to get across is the importance of setting things up properly before you go live because if you don't, it can be very difficult to dig your users out of a stale cache hell hole.
- NEVER cache your index. Set headers to
- Use a bundler (e.g. Webpack) to implement hashed versioned file names.
max-ageheaders on your JS, CSS, and any other files that change frequently.
Set your index headers to
expires 0. This ensures your users get a fresh index every time they launch the app. This is VERY important as your index references all your other files (js, css, etc.) and we want to make sure the correct hashed files are served.
When done correctly, this step should eliminate pretty much all your caching woes. The strategy being when you change something in your file the bundler will also fingerprint the filename by renaming it with a hash (e.g. app.jgm315la0.js). Because the filename is different and because the index is never cached (remember what I said in the paragraph above?) the latest file will be used.
Last, but certainly not least, set your headers for the rest if your files. Using the
Cache-Control response headers you can define how you want a users browser to handle caching. This step is important because if the headers aren't set correctly then your users could potentially end up with stale files...forever! Or at least until they clear their cache, which not a lot of users will know to do. By setting the correct headers you ensure that your users browser will behave as you instruct it to.
Now, there is no hard and fast rule here. Each SPA is different so it's up to you how you handle your
Cache-Control headers. This may be overkill, especially on hashed files because they should be cache-busted when the filename changes, but to start I'd recommended setting
max-age on all your files (apart from index). I say this because you can set them and then change/remove them later when you confirm your hashed files work as you expect, but you can't go the other way if it isn't working as expected and the browser hangs on to that cached version for dear life.
Contrary to its name,
no-cache (which sounds like it should never cache), instructs the browser to always ask the server if the file has changed. If it hasn't, it will use the browsers cached version. This has the unfortunate effect of sending a request, albeit a very very small one, but ensures the browser always checks to see if it should use a cached version rather than just assuming it should always use the cached version.
max-age gives the file an "expiry" date. When the file is downloaded for the first time it is given this maximum age. Once that age comes about, the file is considered stale and will be downloaded again.
When in doubt about how to approach headers for a certain filetype, reference Google's
Cache-Control policy decision tree.
So there you have it. This is by no means a definitive guide and there's a lot to be learned about caching, but hopefully it helps you avoid a painful production experience I just went through. Most importantly, if you follow these initial steps you should be able to easily tweak your headers later and your users won't know any better, but if you don't, they will know all too well.