API caching: control freshness, isolation and consistency

At a glance

A cache avoids some work or exchanges by reusing a response. Its value depends on keys, permissions, acceptable freshness and behavior when the origin is unavailable.

Communications racks and network cabling in an empty equipment room Illustration · fictional scene
In this article

Separate application caching from HTTP caching

Redis, Workers KV and HTTP caches do not automatically share the same guarantees. Workers KV is eventually consistent: a write is not immediately visible everywhere. The directives below concern HTTP caches; they do not configure your application cache by themselves.

HTTP response directiveMain meaningLimitation
no-cacheStorage allowed, validation before reuse.Does not mean “store nothing.”
no-storeDo not store this response in caches.Does not automatically remove earlier copies.
privateNo storage by a shared cache.Not an authentication rule.
max-ageA response’s freshness lifetime.Does not guarantee consistency of all data.

Test the right response for the right person

  • Define key variations: resource, parameters, language and permission scope. Test that one account never receives another account’s private response.
  • Specify maximum age, invalidation and behavior after modification or deletion. A short TTL does not guarantee immediately consistent reads.
  • Test simultaneous expiry, cache failure and origin failure. Coalesce refreshes where needed and serve stale responses only when business rules allow it.

Measure more than the cache hit rate

Track latency, errors, origin load and incorrect or stale responses. Balances, reserved inventory and permissions may need different guarantees from a public catalogue. A fast cache must not become an uncontrolled alternative source of truth.

Primary references

API caching must preserve access decisions

A response cache can make an incorrect query fast when keys ignore requester permissions. Risk rises when a graph combines public and private objects.

  • Classify public, private and role-dependent fields before caching.
  • Test a response under one account and request it under another.
  • Invalidate results after permission or data changes.

Primary reference: OWASP — GraphQL Cheat Sheet. Related method: read the practical guide.

Measure freshness as well as distributed-cache speed

Fictional example: these inputs describe no customer or observed result.

Two reads are fast, but one still shows old availability after the source changes.

  • Compare value, version, reading location and time since change. Test invalidation, expiry and source access against the system contract.
  • Define acceptable freshness per use and monitor divergence. A latency improvement does not establish consistency of served data.

Primary reference: IETF — RFC 9111, HTTP Caching. Related method: read the practical guide.

Dangling subdomains: retire a CNAME without leaving a claimable dependency

Tie each DNS name to a controlled resource and make DNS cleanup part of service retirement.

  • Record public name, CNAME chain, provider resource and owner. Reconcile the DNS zone with the authorized account inventory. An HTTP error, NXDOMAIN or absent target is a finding to investigate, not proof of takeover eligibility; provider protections differ.
  • For a retiring service, remove the mapping or move it to a destination you control before releasing the resource. Account for previous TTLs and caches; check authoritative DNS and several resolvers. Preserve provider ownership validations that are still needed instead of deleting all TXT records.
  • Check links, redirects, OAuth callbacks, webhooks and cookie domains still using the name. If the target was already released, correct the mapping and investigate possible exposure through the incident process. Reconcile DNS and resource inventories after every deletion.

Acceptance case to reproduce

Fictional example: these inputs describe no customer or observed result.

View case inputs
{
    "name": "portal.example",
    "cname_present": true,
    "resource_deleted": true,
    "provider_claimability": "not_verified",
    "takeover_observed": false,
    "expected": "remove_or_replace_mapping_and_review_exposure"
}

Expected decision

Fictional example: portal.example still points to a deleted resource. Remove the CNAME or replace it with a controlled target; an absent resource alone does not establish that a third party took over the subdomain.

Microsoft — Prevent dangling DNS entries

Does a CNAME pointing to an error page prove the subdomain can be claimed?

No. Confirm resource state and provider reservation or ownership-validation controls. Do not claim a resource to demonstrate the risk.

Primary reference: Microsoft — Prevent dangling DNS entries. Related method: read the practical guide.

ETag, HTTP caching and concurrent updates

Revalidation does not replace cache isolation

A matching If-None-Match can justify 304 for the selected representation after access checks. It does not by itself select the correct user variant.

  • Separate variant selection, authorisation and validator comparison.
  • Test weak and strong tags with If-None-Match: the comparison is weak.
  • Read no-cache as validation before reuse, rather than no-store.

Explore ETag comparisons and the simulator →

References reviewed · RFC 9110 · §13 · RFC 9111 · RFC 6585 · §3

GraphQL: errors, lists and partial data

GraphQL result: follow the error path

Aliases belong to the result; list indices start at zero. These details determine where a client should flag missing data.

  • Compare absent data, partial data and data: null.
  • Keep the original path even when a parent becomes null.
  • Test permissions, quotas and interface states separately.

Explore GraphQL responses and nullability →

GraphQL Specification · September 2025 · Published edition · September 2025