CDN caching setup: TTL for static assets, API, HTML

Peak loads overwhelm the origin, and users wait for pages to load. We set up caching turnkey: we separate rules for static assets, API responses, and HTML so that the cache speeds up the site rather than breaking dynamic behavior. The share of cache hits grows, origin load drops, and invalidation remains predictable.

Our competencies:

Frequently Asked Questions

Latest works

  • Development of a web application for FEEDME
    Development of a web application for FEEDME
    1365
  • Development of an online store for the company FURNORO
    Development of an online store for the company FURNORO
    1330
  • B2B Advance company logo design
    B2B Advance company logo design
    776
  • Development of a web application for Enviok
    Development of a web application for Enviok
    1077
  • AIDER company logo development
    AIDER company logo development
    1022
  • CRM development for Chasseurs
    CRM development for Chasseurs
    1113

CDN setup where cache does not break dynamic content

Caching on CDN breaks dynamic content not because of short TTL, but because there is no rule: what is acceptable to serve from the edge, and what must reach origin.

When static content, HTML, and API responses are served by one policy, some pages freeze in outdated form, and some miss cache on every request — and origin gets the same load as without CDN.

Cache misses are almost always a consequence of the key. If the key includes all query string parameters and headers that change from user to user, the same file is stored in dozens of copies: hit ratio drops to single-digit percent, and origin load grows multiple times.

The key is built for the meaning of the resource, and extra parameters are stripped at the edge — then repeated requests are truly served from cache.

Static content with a hash in the filename lives long and updates without invalidation: the new file arrives at a new address, and cache purge is not needed at all.

HTML cannot be handled that way — the page is marked as conditionally fresh, with validation by ETag, or given a short lifetime with background revalidation stale-while-revalidate.

API responses are marked by purpose: public references live by a shared term, personal ones are marked private and not cached on shared nodes.

A separate task is the delivery bottleneck. Origin should serve only what the edge cannot serve, and simultaneous requests for the same miss are collapsed into one trip to the backend.

Then stale content is visible predictably and no longer than a specified window, and origin load stops repeating traffic peaks.

We analyze how this is closed in practice: what policies we attach to resource groups and how we verify that cache does not substitute fresh data.

What a well-built cache gives: hits, speed, resilience

Cache hit ratio and page response time

Cache key and TTL are set by content types: for static with a hash in the filename — long, for HTML — short with ETag revalidation, for API — separate, taking into account the authorization header and Vary.

On Cloudflare and analogues, such a set raises cache hit ratio to 85–95% on a typical site, and it, not origin capacity reserve, determines page response time: the response is served by the nearest node, not the application server.

Offloading origin and behavior under peak

Every hit is a request that did not reach origin. During a sale or after publishing material, origin receives an order of magnitude fewer requests, and the queue on it stops being the bottleneck.

The stale-while-revalidate directive adds resilience: visitors get a cached copy while the update goes in the background and does not block delivery.

Class of failures removed along with setup

The rule "a slow or failed origin must not take down the page" is expressed in stale-if-error: on an error response the client gets the last valid copy.

Availability under peak stops depending on whether origin withstood load — and along with setup, a whole class of failures goes away, which usually accounts for the main share of downtime.

Caching options: edge nodes, intermediate layer, hybrid

The choice between edge cache, intermediate layer, and hybrid scheme is determined not by "speed" but by the share of dynamic content in traffic, audience geography, and how painful stale response delivery is.

Edge nodes such as the Cloudflare network, an own caching contour in front of the application, and a combination of both solve different tasks — compare by applicability conditions.

Option When suitable Limitations
Edge cache Static and public pages, audience spread across regions Event-based purge reaches nodes with delay; no access to internal application data
Intermediate cache layer Many private responses and complex purge rules, application homogeneous All traffic goes through one contour — connectivity and fault-tolerance requirements grow
Hybrid scheme Static on edge nodes, dynamic on intermediate layer Harder to maintain: two sets of policies and two places for diagnostics

Static delivery

For static content, the cache key is built by versioned filename, not by path. Then lifetime can be set long and the response marked immutable: a new release brings new names, and old objects live out their term without forced purge.

Publication comes down to uploading files to storage, and the edge network picks them up on first request.

Rules for dynamic content

Here the logic is reverse: short lifetime plus event-based purge. HTML is served with a short term and background update, so the visitor does not wait for page rebuild, and personal responses are either not cached or separated by authorization attribute.

Traffic spikes are absorbed by micro-cache for a few seconds — it removes load from the application and at the same time does not turn data stale.

Work sequence: traffic audit, rules, validation, handover

We analyze not configuration, but facts: how traffic flows now, what is cached on it, and where origin receives repeated load. Without access logs and current response headers, any rule change is guessing. Therefore we conduct work in stages, where each step is verifiable and reversible.

  1. Traffic audit. Take access logs for a representative period, calculate the share of repeated requests and cache hits by content groups. Separately write down what cannot be cached: authorized sections, responses with personal data, cart and checkout.

  2. Route map. Map address templates, query string parameters, and headers that affect the cache key. Record places where different responses are served at one address — this is the main source of "stuck" pages and foreign data in cache.

  3. Caching rules. Set separate TTLs: long for static and media, short for HTML with mandatory freshness validation, targeted for API — only where the response is the same for everyone. Write cache control headers on the application side, not only in delivery network settings.

  4. Purge and deployment. Configure purge by paths and tags, tie it to release: after publishing a new build the user gets fresh files, not a version from cache.

  5. Validation on production traffic. Enable rules on part of traffic, compare hit ratio, response codes, and discrepancies with origin. Rollback — with one configuration change, without application deploy.

  6. Handover of procedures. Provide documentation on rules, release checklist, purge scheme, and set of metrics to monitoring, so the team can further change TTL and routes independently.

Configurations, invalidation rules, and deliverables

Setup ends not with a report, but with a set of artifacts that the customer's team works with without us.

Configuration is in a repository, accesses are separated, and cache behavior is described so it can be reproduced on another environment and verified without the author of changes.

  • Configuration files in version control: caching rules by content types, Cache-Control and Vary headers, s-maxage and stale-while-revalidate values, edit history and rollback.
  • Invalidation rules: purge by tags and path prefixes after release, full purge — only as an emergency operation, separate order for static, API responses, and HTML.
  • Request flow scheme: what is served from the nearest node, what goes to origin, at what step headers are added, and how cache hit ratio is calculated.
  • Access separation for the control panel: roles for development, support, and analytics, prohibition of full purge without approval, action log for each change.
  • Support instructions: procedure for cache purge, freshness check after deploy, and sequence of actions for a stale response — with commands and expected result.
  • Content type to TTL mapping: which extensions and routes are assigned to each profile — so a new site section does not fall out of the scheme when added.

Case: how we removed drops and reduced origin load

Before: cache was configured "by eye"

Project — a storefront with catalog and personal account: static, HTML pages, and API behind one CDN. About 40% of requests were cached, and TTL was the same — an hour for everything, including API responses with personal data.

During peak hours origin received almost the entire flow: 95th percentile response time reached 1.9 s, and after each deploy the duty engineer manually purged cache by dozens of paths, sometimes forgetting part — users saw old price and old availability.

What changed

We separated rules by content types. Static is served with long TTL and versioning in the filename — update requires no purge at all. HTML is cached briefly, with a stale copy served while rebuild pulls a fresh one.

API responses are cached only where the key includes user and request parameters, everything else marked private. Instead of manual purges — tags on content and one invalidation call per deploy: Cloudflare rolls it out across nodes itself.

After

Share of responses from cache grew to 94%, origin requests fell fourfold, and response time under the same peak — from 1.9 to 0.3 s. Manual purges became one or two per week instead of two hundred, and almost all are a standard deploy step.

Delivery stopped "flickering": the expired copy is served instantly while the fresh one travels from origin, so the user sees neither a blank screen nor an old price after catalog update.

How does caching static content differ from API when configuring CDN?

Static content and API responses become stale at different speeds: a build file with a hash in the name does not change until the next release, while an order list loses relevance in seconds.

One rule for the entire domain gives either stale data in the interface or a cache that serves nothing.

Content type Record lifetime Freshness validation
Builds and images with hash in name until next release not needed: name changes with file
HTML and catalog pages minutes conditional request by ETag, 304 response
Public API responses seconds freshness validation or serving old copy with background update
Authorized responses not cached bypass cache by authorization header

Separation of rules is done not for the entire domain, but at the level of paths and content types: static has a long lifetime, API has a short one with mandatory validation.

The cache key takes into account the address together with query parameters and language, so output by filters and locales does not mix, and authorized responses are taken out by a separate rule.

Freshness validation separates freshness from re-download: the node serves a 304 response while content has not changed, and the full object — as soon as it changed.

With a short record lifetime, the first request after expiration goes to origin, the rest receive an already prepared copy.

Invalidation is tied to release: during deploy only affected paths or tags are purged, not the entire cache. Thus a long term for static and a short one for API coexist in one scheme without manual edits.

Doubts at start: invalidation, private data, debugging

Cache purge, private sections, and checking that cache actually works — three questions that come up already at the policy design stage, not after launch. Below are short answers to them.

How to purge cache if a bad release went out?

Targeted invalidation by paths and tags is a working tool, but it is not instant: on cache nodes, the copy lives until its term expires.

Therefore we build policy so an error can be removed without affecting the rest: static and scripts are published with version in the filename, and the old version is displaced by itself as links update.

Will private data leak into shared cache?

Private sections, personal account, and authorized requests we do not put into shared cache at all — this rule is fixed at configuration level, not on developers' discipline. The logic is simple: if a response depends on who requested it, the cache node has no right to store it. Only what is the same for everyone remains public.

How to make sure cache works as intended?

Cache observability is hit ratio broken down by content types: separately static, separately pages, separately backend responses. If hit ratio is high for images and styles, but suddenly dropped for pages, then the rule is broken or a new parameter appeared in the address.

We output these metrics to the dashboard together with origin server data — you can see what share of traffic does not reach it at all.

What to do during an incident: some users see old version?

Incident analysis starts with comparison: what the cache node served and what origin served for the same address. Then we look at which node retained the old copy, remove it in a targeted way, and fix the rule that let it survive the update.

Most often the cause is a forgotten tag or too long term for an address that changes with every release.

Let's discuss your caching scheme and we will return with a cost calculated after audit

The accurate cost calculated after audit and the work plan rest on understanding how your traffic is structured and how you publish content.

Below is what we analyze in the first conversation: without these inputs any caching policy turns out average and either overpays for misses or starts serving stale pages.

We'll discuss the task concretely — for each point, your node statistics, access to configuration, and operational experience are sufficient.

  • Traffic profile: shares of static, API requests, and markup, daily peaks, ratio of unique to repeated requests — achievable cache hit ratio depends on this.
  • Content types and acceptable staleness: catalog, feed, personal and authorized responses — what can be kept in cache and what must go to origin.
  • Origin limitations: throughput reserve, limits on number of requests, and behavior under mass cache miss after purge.
  • Cache purge requirements: how quickly a change must become visible, whether path versioning or group tags for invalidation exist.
  • Publication scheme: how release reaches nodes and how this aligns with invalidation, so mixed versions are not served.
  • Accesses and rights: where configuration is stored, who changes rules, and how TTL changes are recorded.
  • Support mode: whether hit ratio monitoring, alerts on origin traffic growth, and policy edit procedure are needed.

Describe the task — we will return with a cost calculated after audit and a work plan: TTL layout by content types, invalidation order, and monitoring scope.