Controlling Akamai CDN Caching
An Akamai property in front of a Zuplo gateway can absorb most of the read traffic an API serves. A catalog endpoint that returns the same payload to every caller doesn't need to reach the gateway 40,000 times an hour. It needs to reach it once every 30 minutes, and again the moment the catalog changes.
The CDN Cache Control policy writes the headers Akamai reads to make that
happen: Edge-Control for the edge TTL, and Edge-Cache-Tag for the purge tags
that let a content update invalidate the edge in seconds instead of waiting out
the TTL.
How Akamai caching works
What the edge does depends on the property configuration and on the headers Zuplo sends:
- Akamai Property Manager settings. The default caching rules configured in your CDN property.
- Origin response headers. What Zuplo sends, which can override or influence those defaults once the property is configured to honor them.
The edge TTL and the purge tags travel in headers the client never acts on, so
the response the client receives carries only Cache-Control. Akamai also
strips Edge-Cache-Tag before responses leave the edge unless the Cache Tag
Visibility behavior is enabled, so tags stay internal by default.
For complete details on Akamai's caching behavior, see the Akamai caching documentation.
Set the edge TTL and purge tags
Add the CDN Cache Control policy to the outbound pipeline of any route whose
responses are safe for a shared cache to replay. Set cdn to akamai and the
policy renders everything else into Akamai's dialect.
Code
Akamai receives:
Code
The edge holds the response for 30 minutes and absorbs the traffic. Clients
re-check every 60 seconds, so a purge of the cities tag reaches every caller
within a minute rather than within half an hour.
The policy also removes an Edge-Control header your backend set that the
policy is not writing itself, because a targeted header outranks Cache-Control
and an upstream value would silently beat the TTL configured here.
respectUpstream is on by default. An upstream Cache-Control of no-store,
no-cache, or private, or a Set-Cookie on the response, suppresses every
edge header and purge tag. The backend keeps the final say even when the policy
is applied broadly.
Splitting the edge TTL from the client TTL is the point of the policy. Because
only Akamai reads Edge-Control, Cache-Control stays free to say whatever the
client should do, down to private, max-age=0 for a response that requires an
Authorization header but is identical for every caller:
Code
Akamai reads Edge-Control and ignores Cache-Control for its own decision, so
it never sees the private. The edge caches; nothing downstream does. For the
reasoning behind the split and the variations on it, see
Cache at the CDN.
Don't put the Caching policy on the same route
Keep the Caching policy off any route that
uses CDN Cache Control. The Caching policy serves a hit from an inbound policy,
which short-circuits the pipeline before outbound policies run, so CDN Cache
Control never executes on a gateway cache hit. Akamai then receives the copy
that the Caching policy stored, and the Caching policy sanitizes every copy it
stores: it drops several CDN cache headers (exactly which ones depends on the
CDN) and overwrites Cache-Control with its own s-maxage (its TTL, 60 seconds
by default). The edge and client TTLs collapse into one number. Cache misses run
the full outbound stack and look correct, so the problem stays invisible until
traffic warms the cache, and nothing logs it. Pick one place to cache.
Akamai-specific constraints
Stale serving needs Property Manager
edge.staleWhileRevalidate and edge.staleIfError have no Edge-Control
equivalent. Rather than dropping those directives silently, the policy rejects
the combination on Akamai. Two ways forward:
- Configure stale serving in Property Manager. Leave
edge.staleWhileRevalidateandedge.staleIfErrorunset, keepstrategyat its default, and let the property decide how long the edge may serve a stale object while revalidating or while the origin is failing. - Set
strategytos-maxage. The edge TTL and the stale windows then travel inCache-Control, which works on a property that honors originCache-Controlbut has not enabledEdge-Control.
Code
s-maxage gives up the split: every directive becomes visible to clients, and
caching at the edge without caching in the browser is no longer expressible.
Prefer Property Manager for stale serving when the property already honors
Edge-Control.
Vary suppresses caching
The vary option is off by default because of a behavior specific to Akamai:
the edge skips caching any response carrying a Vary header until Cache ID
Modification is configured in Property Manager. Turning vary on can therefore
disable edge caching entirely while every response still looks correct. The
policy logs a warning when vary is set together with cdn: akamai.
If a response genuinely varies by a request header, put that header in the cache
key with
Cache ID Modification
instead of relying on Vary. Cache-key configuration is not expressible in
response headers and stays CDN configuration.
Purge tag limits
Tags are validated against Akamai's limits before they are emitted, because the failure mode at the edge is silent truncation rather than an error.
| Limit | Akamai value |
|---|---|
| Maximum tag length | 128 characters |
| Maximum tags per response | 128 |
| Maximum header size | 8192 bytes |
| Disallowed characters | spaces, commas, colons, brackets |
A static tag in config/policies.json that violates a limit is a configuration
error you fix before deploying. A tag produced at request time is dropped and
logged as a warning instead.
Purge by tag with Fast Purge
Tagging responses is what makes targeted invalidation possible. A city sync that
changes one record can invalidate city-42 across every edge server, leaving
the rest of the catalog cached. Without tags, the choices are waiting out the
TTL or purging far more than changed.
The policy emits the tags. It does not purge. Invalidation is a Fast Purge API call from your own tooling: a webhook on a content change, a step at the end of a sync job, or an operator-run script. Point it at the invalidate-by-tag endpoint with the same tag strings the policy emits.
Two Akamai details worth knowing:
- Akamai strips
Edge-Cache-Tagfrom responses before they reach end users unless the Cache Tag Visibility behavior is enabled. Enable it temporarily to confirm tags are landing, then turn it back off. - Tag names must respect the charset and limits above. See Akamai's guide to assigning cache tags.
Per-response cache rules
Static options cover a route. Per-entity tags such as city-42, a TTL that
depends on a response header, or refusing to cache an empty result set cannot be
expressed statically. For those, the policy takes a cacheConfig function that
returns cache intent per response, and falls back to the static options whenever
the function returns nothing.
The function returns intent rather than headers, so the same module works unchanged if the CDN in front of the gateway changes. See Per-response cache rules for the contract, the return values, and worked examples.
Configure Property Manager
None of the headers above matter until the property is configured to act on them. This is the part you cannot do from Zuplo.
- Caching behavior. Set the Caching Option to "Honor origin Cache-Control
and Expires" and enable the directives you want honored (
max-age,s-maxage,no-store,private,must-revalidate). Without this the property applies its own fixed TTL and ignores what the gateway sends. - Honor
Edge-Control. Confirm with your Akamai team that the property acts onEdge-Control.Edge-Control: cache-maxageoutranksCache-Controlat the edge, which is what keeps the edge TTL out of the client's copy. If the property ignores it, setstrategytos-maxageuntil it is enabled. - Default Max-age. Set a fallback TTL for responses that arrive with no cache headers, such as routes that do not carry the policy.
- Cache ID Modification. Configure this if a response varies by a request
header. It is also the prerequisite for caching any response that carries a
Varyheader. - Cache Tag Visibility. Off by default, which keeps
Edge-Cache-Taginternal. Turn it on only while verifying that tags reach the edge.
For detailed CDN setup instructions, see Setting up Akamai CDNs.
Downstream cacheability
By default, Akamai sends clients the smaller of the origin's Cache-Control
max-age and the remaining edge cache lifetime, so browsers never cache content
longer than it remains valid at the edge. With the policy in place, the origin
max-age is whatever client.maxAge sets; the edge TTL lives in Edge-Control
and never reaches the client at all.
Property Manager's downstream cacheability settings can override this. For more information, see Akamai's downstream cacheability documentation.
Best practices
- Make the client TTL much shorter than the edge TTL. The edge is what you
can purge; clients are not. A short
client.maxAgewith a longedge.maxAgegives you offload and control at the same time. - Tag by entity, not just by route.
city-42lets you purge one city;citiesalone forces you to purge them all. - Leave
respectUpstreamon so the application keeps the ability to mark a response uncacheable. - Verify at the edge, not just at the gateway.
curl -Iagainst the Zuplo URL shows the headers the policy emits. Whether Akamai honors them depends on the property configuration above.
Related resources
- Cache at the CDN — the CDN Cache Control policy across every supported CDN
- Caching in Zuplo — where to cache, and how to choose
- CDN Cache Control policy — every configuration option in detail
- Caching policy — the gateway-side alternative, not to be combined with CDN Cache Control
- Akamai caching documentation — the complete Akamai caching reference
- Akamai Fast Purge — purge by tag from your own tooling