CDN Cache-Tag Headers
Emit a Cache-Tag response header on every frontend HTML request, giving your CDN per-resource tags to purge against - “flush everything tagged lang:fr_FR” or “flush post:42” without nuking the whole edge cache.
Disabled by default. Enable via PerfLocale → Settings → Advanced → CDN Cache-Tag Headers, or programmatically with add_filter( 'perflocale/cache_tags/enabled', '__return_true' ). When off, no send_headers callback is registered and no class is loaded.
What gets emitted
One Cache-Tag header per response, with a comma-separated list of tags for that request’s context:
Cache-Tag: perflocale,lang:fr_FR,lang-slug:fr,post:42,post-type:post
| Always | Per-context |
|---|---|
perflocalelang:<locale>lang-slug:<slug>
|
Singular: post:<id>, post-type:<type>Term archive: tax:<taxonomy>, term:<term_id>Post-type archive: archive:<type>Author archive: author:<id>Date archive: date:YYYY-MMSearch: search404: 404Home/front page: home
|
CDN support matrix
| CDN | Header name | Out of the box? |
|---|---|---|
| Cloudflare (Enterprise Cache Tags) | Cache-Tag | Yes |
| Bunny.net | Cache-Tag | Yes |
| Fastly | Surrogate-Key | Filter perflocale/cache_tags/header_name |
| KeyCDN | Cache-Tag | Yes |
| Akamai | Edge-Cache-Tag | Filter perflocale/cache_tags/header_name |
For Fastly:
add_filter( 'perflocale/cache_tags/header_name', fn() => 'Surrogate-Key' );
Sanitisation + limits
- Tags are filtered to
[A-Za-z0-9\-_:.]only - no whitespace, quotes, commas, or control characters - Per-tag length capped at 128 characters
- Maximum 32 tags per response
- Header value is hard-capped at 8000 bytes (filterable via
perflocale/cache_tags/max_header_length); tags overflowing the budget are dropped silently - Header is never emitted on admin, AJAX, cron, REST, XMLRPC, or feed requests; also skipped for paths in the routing-exclusion list
Customising the tag list
add_filter( 'perflocale/cache_tags/tags', function ( array $tags ): array {
$tags[] = 'theme:' . get_template();
if ( is_singular( 'product' ) ) {
$product = wc_get_product( get_queried_object_id() );
foreach ( $product->get_category_ids() as $cat_id ) {
$tags[] = 'wc-cat:' . (int) $cat_id;
}
}
return $tags;
} );
The filter runs after PerfLocale’s sanitisation pass, so your custom tags go through the same [A-Za-z0-9\-_:.] guard before emission.
Purging from your CDN
PerfLocale does not issue remote purges - that’s host-specific and there are plenty of existing plugins for each CDN. Instead, PerfLocale fires an action with the tags that need flushing:
add_action( 'perflocale/cache_tags/purge', function ( array $tags ): void {
// Cloudflare example via an existing integration plugin.
do_action( 'cloudflare_purge_tags', $tags );
} );
This fires on relevant content-change hooks (post save, term edit, translation linked) so sibling-language variants get purged alongside the source content.
Disabling per path
If you need to suppress the header for specific routes without turning the whole feature off, add the path to Settings → URL & Routing → Excluded Paths. PerfLocale uses the same exclusion list for routing and for Cache-Tag emission.
Related
- Hooks reference - all four filters + the purge action
- Edge integration - pair Cache-Tag headers with edge-based language routing for near-100% edge cache hit rates