Cache-Tag
The HTTP Cache-Tag response header is an unofficial header labeling a cached response with one or more identifiers, so a later purge removes every response sharing a label rather than one URL at a time.
Usage
An origin attaches Cache-Tag to a response and Cloudflare stores the labels alongside the cached object. Purging by tag then clears every object carrying the label in a single operation, which suits content appearing across many addresses. A product record rendered on a category page, a search result, and a detail page carries the same tag on all three, so one purge refreshes the set.
The alternative, purging by URL, requires knowing every address where the content appears. Tags move the bookkeeping to publication time, where the rendering code already knows which entities went into the response.
Cloudflare strips Cache-Tag before the response reaches a visitor. Browsers and Workers see nothing, so the header stays a private channel between the origin and the edge. Confirming the value calls for a request straight to the origin, bypassing the proxy.
Values
Tags are comma-separated and accept printable ASCII
only, with spaces prohibited inside a tag. Whitespace
around a separator is stripped, repeated commas
collapse, and matching ignores case, so Tag1 and
tag1 address the same objects. A response carries
more than one Cache-Tag header where the list
grows long.
Cache-Tag: product-1234,category-shoes,homepage
Three limits govern the header:
| Limit | Value |
|---|---|
| Tag length in the header | no documented maximum |
| Tag length in a purge API call | 1,024 characters |
| Aggregate header size | 16 KB after the field name |
The 16 KB budget counts whitespace and commas while excluding the field name, and Cloudflare estimates roughly 1,000 tags inside the allowance. The figure is a gloss on the byte cap rather than a separate count, so the real ceiling tracks tag length.
Exceeding the 16 KB aggregate is the common failure, and generous per-entity tagging on a page assembling hundreds of records reaches the ceiling quickly. Tagging by entity type and identifier rather than by every relationship keeps the header inside the budget.
Example
A product page labels the response with the product, the category, and the template. A change to the category purges every product carrying the same category tag, and a template change purges every page built from the same template.
HTTP/1.1 200 OK
Content-Type: text/html
Cache-Control: public, max-age=3600
Cache-Tag: prod-88213,cat-outerwear,tpl-product-v3
Ecommerce platforms in the wild favor short prefixed identifiers rather than readable names. Observed values follow a compact scheme, where each prefix marks an entity type and the number identifies the record.
Cache-Tag: P-20927937,SS-58645295751,PGS-ALL,SW-3
The compactness is deliberate. Short prefixes keep hundreds of tags inside the 16 KB budget, and the purge API matches on exact strings, so readability buys nothing at the edge.
Purging by tag
Purge requests go to the Cloudflare API for the zone, carrying the tags to clear.
POST /client/v4/zones/{zone_id}/purge_cache
Authorization: Bearer <token>
Content-Type: application/json
{"tags": ["prod-88213", "cat-outerwear"]}
Purging by tag is available at Cloudflare. Rate limits and batch sizes vary by Cloudflare account, with a maximum of 100 operations per request. A deployment purging thousands of tags batches them and paces the calls against the account limit rather than firing per entity.
Purged objects return to
Cf-Cache-Status MISS on the
next request, or EXPIRED where Tiered Cache leaves
a lower tier revalidating against an upper one.
Propagation is fast enough to treat as immediate,
with Cloudflare reporting a median under 150ms
globally, ahead of single-URL purging.
Equivalent headers across CDNs
Every major CDN implements the same idea, and three details differ between them: the header name, the separator, and whether the header survives to the browser.
| Vendor | Header | Separator | Downstream |
|---|---|---|---|
| Cloudflare | Cache-Tag |
comma | always stripped |
| Netlify | Cache-Tag |
comma | always forwarded |
| Akamai | Edge-Cache-Tag |
comma | configurable |
| Fastly | Surrogate-Key |
space | stripped unless debugging |
Netlify is the case worth attention, because the
header name and separator match Cloudflare exactly
while the behavior inverts. Netlify forwards
Cache-Tag downstream deliberately, so other
caches read the labels, and accepts UTF-8 where
Cloudflare accepts printable ASCII alone. Netlify
also caps a response at 500 tags and offers
Netlify-Cache-Tag, which is stripped and takes
priority when both appear.
Stacking the two providers exposes the mismatch. A Netlify origin behind Cloudflare emits tags intended for the browser into a proxy treating the same header as a private purge instruction, and the labels disappear at the Cloudflare edge.
Akamai diverges by name rather than by meaning, and
Edge-Cache-Tag caps a tag at 128 characters and a
cached object at 128 tags, well below the Cloudflare
budget. Only the first Edge-Cache-Tag header on a
response is respected, and a 304 response leaves
existing tags unchanged.
Crawl data shows platforms hedging. Responses
carrying identical comma-separated values in both
Cache-Tag and Edge-Cache-Tag appear regularly,
which is how one origin serves customers behind
either CDN without branching the rendering code.
Migrating tags from another CDN
Cloudflare Cache Response Rules build Cache-Tag
from an existing header through the set_cache_tags
action, so tagging code written for another CDN
survives the move. The translation runs only once
configured, with no implicit conversion.
Cloudflare's documented example splits a
Surrogate-Keys header on commas. A migration from
Fastly needs different values on both counts, since
the Fastly header is Surrogate-Key without the
plural and separates with spaces. Copying the
published example unchanged against real Fastly
output produces a single malformed tag rather than a
list, and purges silently match nothing afterwards.
Standing outside the registry
Cache-Tag is a vendor convention rather than a
registered field, as are Edge-Cache-Tag and
Surrogate-Key. Purge-by-label has no standardized
header, which is why four vendors arrived at four
answers.
A standards-track alternative now exists in Cache-Groups and Cache-Group-Invalidation, which describe group membership and invalidation as registered fields rather than per-vendor headers. Adoption remains early, and vendor tagging carries production traffic today.
Labelling stays orthogonal to cacheability throughout. Cache-Tag marks a stored object for later purging and changes nothing about whether the object is cached or for how long, which remains the work of Cache-Control and the targeted headers.
See also
- Cloudflare: Purge cache by cache-tags
- Surrogate-Key
- Cache-Control
- CDN-Cache-Control
- Cf-Cache-Status
- Caching
- HTTP headers