How to test for HTTP stale-while-revalidate support?

stale-while-revalidate is a useful extension to the HTTP Cache-Control response header. The header instructs intermediary caches that they can fulfill requests by returning resources that have expired from the cache for some additional time. The cache can then asynchronously perform a revalidation or update if the cached resource. This way, the visitor won’t have to wait for an edge server to update a resource before delivering it.

Sounds neat, right? But how do you test that this work? As for many other problems in the world, this is one that you need to sleep on.

Lately, I’ve been evaluating a few different content delivery networks (CDN), and have found the market is full of exaggerations and lies about the networks’ technical capabilities. Documentation is also quite lacking for many service providers. I found it necessary to test and confirm their claims on my own, and had to find a reliable way to test whether stale-while-revalidate (RFC 5861) worked asynchronously from a given intermediary cache.

The test setup

You’ll need a simple test script, see test.php included below, that returns the appropriate Cache-Control headers for the test and that introduces a measurable delay that can be used for testing.

<?php
  header('Cache-Control: max-age=20, stale-while-revalidate=80');
  sleep(10);
  print('This is a slow test.');

The test page sets the Cache-Control header with a freshness of 20 seconds, and allows intermediary caches like a reverse proxy, content distribution network (CDN), or web accelerator to serve stale copies for an additional 80 seconds. Before the test returns, it waits for 10 seconds. When stale-while-revalidate is working, no request/visitor should have to wait for ten seconds before the request has completed as long as the page remains in the cache (which in this case is 20+80 seconds.)

You can easily test this assumption using the Apache benchmarking tool (ab.) Request the test page once to populate the cache, this first request will have to wait for the full 10 seconds. The subsequent requests should be served from the cache and thus all return within a few milliseconds.

ab -s50 https://www.example.com/test.php && \
ab -s50 -n2000 -v2 https://www.example.com/test.php

If you see any results in the second test run that takes 10 seconds or more, the intermediary cache doesn’t support asynchronous stale-while-revalidate update. Either the server doesn’t support stale-while-revalidate at all, or you’re testing against Nginx.

You can test if subsequent requests are served stale copies while one request is held up by adding c2 to the test to run two concurrent test runs.

I’ve tested RFC 5861 support at a few content delivery networks (CDN), and published the results if you’re interested.

Notes on stale-while-revalidate in Nginx

You can enable stale-while-revalidate in Nginx by setting the proxy_cache_use_stale option to include updating. By default, Nginx will mimic Apache’s infamous “thundering herd” mitigation solution: the first request to an expired resource in the cache will serve as the cache lock while the resource is being revalidated or updated. Subsequent requests while the lock is in place will be served the stale file directly from the cache. However, this still means that one request/visitor will see slower response times while the cache is updated.

To allow fully asynchronous stale-while-revalidate handling for all visitors, you also need to set the proxy_cache_background_update option to on. This option is a fairly new addition and only recently appeared in Nginx version 1.11.10. If you repeat the above test with this option enabled, you should no longer see any slow requests.