This post will be about a new caching technique i recently learned about and use in the projects i am working on, including this very blog. The concept is called stale-while-revalidate and will result in loading times that are blazing fast all around the world.
How does it work?
When you use a CDN-proxy in front of your website all of your websites traffic will go through that proxy. Examples for this are Cloudflare, AWS Cloudfront and the smart ZEIT Smart CDN.
This proxy will try to figure out how to cache your websites traffic based on HTTP-headers. The most important header for this blog post will be Cache-Control. It usually specifies how long your browser should cache a certain asset or image. The new stale-while-revalidate directive tells the proxy to always serve a stale version of a file (if possible) and revalidate the cache in the background. This results in fast load times all around the world because the user will always get a already cached version of a file. Even if the request would take multiple seconds to resolve, with the help of the proxy the user always gets his response blazing fast.
I recommend to use the Cache-Control header with the following directives:
Cache-Control: s-maxage=1, stale-while-revalidate
The stale-while-revalidate directive tells the proxy to revalidate the cache in the background. At the same time s-maxage=1 makes sure to only revalidate once every second. This means even when there are thousands of requests hitting your domain in one second, the proxy will only make one request to your server and validate if the cache needs to be updated. That behavior makes serving your content incredibly cheap and fast at the same time.
Be aware that the cache needs time to get hot. The very first request will be served synchronously because the proxy has to create a new cached version first, after that all subsequent requests are served from cache. If the cache is stale, revalidation will happen asynchronously in the background. If the cache is stale is dependent on the s-maxage=<seconds> value. To always get the latest version i recommend to use it with a value of 1, which means one second.
At the time i am writing this post i am sitting in Bali and my server that is running the Ghost instance is back in Germany. A ping gives me a latency of ~200 ms.
After i make a request to https://lucas.love you can see that it automatically uses the nearest CDN, wich in my case is in Singapore. The request only takes up 271 ms.
Let's try the Pragma: no-cache header. This will tell the CDN to skip the cache and make a new synchronous request to the origin server. It will simulate the the same behavior as if it would be the very first request that hits the CDN with a cold cache.
You can see that the request takes up a lot more time, 1487 ms to be precise. Way slower than the request before. This happens because my server first has to look up some stuff in it's database and responds over a second later (Server Processing).
One additional positive side-effect to know is that it also helps with having fewer downtimes. If the proxy is not able to get a fresh version of your file because your origin server is currently down, it will still give all clients the latest working copy without any form of interruption.
Because the client will always get a stale version it is possible that a user visiting your site will get a very old version that for example does not contain the latest changes you made to your blog.
To avoid this you have to build your own system around that problem. lucas.love for example tells your browser to make a XHR request with the Pragma: no-cache header to the same URL you are currently visiting after the page finished loading and when you reopen the browser tab. After the request was made the browser will compare a header called x-head that contains a hash of the contents of the page. If this header does not match, your browser will reload the page with the latest copy available. Because the proxy will use your XHR request to revalidate the cache this will happen so fast that your users probably will not even notice it.
Stale While Revalidate is a very powerful concept. It takes load of your server and at the same time makes your site faster for all users around the world. I would recommend to use this directive for the following use-cases:
- Blogs and Newspapers: Your content may change infrequently but you still have the flexibility to fix typos for example. You don't have to wait for the cache to expire.
- APIs: The content may change frequently but takes a significant amount of time to generate. For example low database queries and high latencies.
- Public dashboards: Lots of users consume loads of dynamic content.
- Webshops: with lots of products and categories.
- Landing page: Changes infrequently but has a lot of users that access it at the same time.
Do not try to use the stale-while-revalidate when content of your site can not be cached. For example when you have to deal with user authentication or have to render user specific content like on a social network. If you can't use a cache, stale-while-revalidate is not the right use-case for your project.