APO stands for Automatic Platform Optimization and it works great for WordPress content. For the most part, cache invalidation is handled automatically as it says in the name.
You can bypass Cloudflare APO’s cache for specified WordPress paths in a few ways. The details of how to do that are covered elsewhere.
However, if you serve static content that resides outside of WordPress, getting Cloudflare to serve browser nocache headers can be a challenge, and it wasn’t something I personally found answered elsewhere.
Exactly why you might need browsers to not cache static assets is not considered here; my project has its reasons, and you have your own. Generally, caching is a great thing, until it’s not.
Here’s the headers we need in order to prevent browsers from caching a copy:
Cache-Control: no-store, no-cache, must-revalidate, max-age=0
Expires: Wed, 11 Jan 1984 05:00:00 GMT
- no
Pragma
👎
Cache-Control
has max-age=0
because that tells Cloudflare that its cached copy is stale.
Expires
shouldn’t be necessary for browsers to not cache, but it’s required here for Cloudflare.
I like to keep things tidy, but Pragma
is your call. Depending on who’s writing or when they wrote it, it’s either strictly necessary or it will get you laughed off the internet.
General note regarding cache headers: There’s a lot of bad / outdated information on the internet, so you should to do your own testing to make sure it works with your setup. It’s the only way to be sure.
Here’s my setup:
- WordPress
- Nginx
- Cloudflare APO
If you’re doing server side page caching via FastCGI page cache or similar, you’re ahead of the game and you already knew you need to specify page cache bypass for your static assets you don’t want cached.
Here’s what worked for me:
add_header
in your nginx config, in a location block matching your static files (~ regex or ^~ match both work). I recommend using a sites-enabled config file.cf-apo-via: origin,page-rules
header must be sent from your server- In the Cloudflare Dashboard, create a Cache Rule:
- Cache eligibility => Bypass cache
- Browser TTL => Override => No cache
- and create a Page Rule:
- Browser TTL => 2 mins
- Cache Level => Bypass
I think the cache/page rules are needed because:
By default, Cloudflare honors the cache expiration set in your Expires and Cache-Control headers but overrides those headers if:
https://developers.cloudflare.com/cache/how-to/edge-browser-cache-ttl/#browser-cache-ttl
- The value of the
Cache-Control
header from the origin web server is less than the Browser Cache TTL Cloudflare setting.
I think this makes sense because of the way Cloudflare rules interact with APO.
Cloudflare APO, page/cache rules, origin headers, browser TTL…
…and that’s about all I really understand. If anyone knows exactly why this works, or if anything here is unnecessary, get in touch.
Further reading:
- https://nginx.org/en/docs/http/ngx_http_core_module.html#location
- https://developers.cloudflare.com/automatic-platform-optimization/reference/page-rule-integration/
- https://developers.cloudflare.com/automatic-platform-optimization/troubleshooting/faq/#why-are-my-browser-cache-control-headers-missing-with-apo
- https://developers.cloudflare.com/cache/concepts/cache-control/#interaction-with-other-cloudflare-features
- https://developers.cloudflare.com/cache/how-to/edge-browser-cache-ttl/#browser-cache-ttl
- bonus 🙂 https://stackoverflow.com/questions/54012749/why-does-wordpress-use-11-jan-1984-as-an-anti-caching-value-for-expires-headers