Skip to main content

Command Palette

Search for a command to run...

Content Delivery Network & How to scale frontends

Updated
7 min readView as Markdown

"Scaling the frontend" splits cleanly into two problems that are nothing alike. A static frontend is just prebuilt files, and files scale almost for free. A dynamic frontend renders HTML on a server for each request, which costs compute and is where the actual scaling work lives. And sitting in front of both is the single biggest lever you have: a Content Delivery Network. This post covers how to scale each kind of frontend and how a CDN carries the load - the caching chapter, applied to the edge.

Static frontends scale like files, because they are files

A static frontend - a site built ahead of time with static site generation (SSG), or a single-page app whose build output is HTML, CSS, and JS - produces the same bytes for every user. There is no per-request computation, so "scaling" it means one thing: serve the files from somewhere that is cheap, distributed, and close to users. That somewhere is object storage plus a CDN. You upload the build to object storage (the same S3-style storage you would hand out presigned URLs for), point a CDN at it, and you are effectively done - the CDN replicates your files to edge locations worldwide and serves them from memory near the user. This scales to enormous traffic on a rounding error of a budget, because you are not running servers, you are handing out cached files.

The techniques that make static delivery even cheaper are all about serving fewer, smaller, more-cacheable bytes:

  • Hash your asset filenames (app.9f2a.js) and mark them immutable with Cache-Control: max-age=31536000, immutable. The content never changes for a given hash, so browsers and the CDN can cache it forever; a deploy just produces new filenames.
  • Code-split so a route ships only the JS it needs, and compress with gzip or Brotli at the edge.
  • Optimize images (modern formats, responsive sizes) and lazy-load below-the-fold assets.

Each of these cuts origin load and transfer size, which is what "scale" means on the static side.

Dynamic frontends cost compute, so you cache and spread it

A dynamic frontend renders HTML per request - server-side rendering (SSR) - because the page depends on data or the user. That HTML is real work on a real server, so the naive version does not scale: every request hits your render tier. You attack it on three fronts.

Render statelessly and scale horizontally. An SSR server holds no per-request state, so run many identical instances behind a load balancer and add instances as traffic grows. This is standard backend scaling applied to the render tier.

Cache the rendered output. Most "dynamic" pages are not unique per user - a product page is the same for everyone for minutes at a time. So cache the rendered HTML at the CDN edge and serve it without re-rendering. Incremental Static Regeneration (ISR) is the pattern that formalizes this: serve a cached static page, and regenerate it in the background on a schedule or on demand, so you get static-fast delivery with fresh-enough content. Pair it with the stale-while-revalidate cache directive, which serves the stale copy instantly while fetching a fresh one behind the scenes.

Render at the edge. Instead of one central render tier, run the SSR at the CDN's edge locations so the HTML is generated near the user - cutting the round-trip to a distant origin. Modern frameworks lean on this together with streaming (React Server Components) to send HTML as it is produced.

The real lesson is that rendering is a per-route decision, not a global one. Use SSG or ISR for content that is the same for everyone (marketing, product, blog), SSR only for pages that genuinely need fresh, request-specific data, and client-side rendering for authenticated, highly interactive views. Frameworks like Next.js and Astro let you mix these per page - lean static, and reach for SSR only where you must.

The CDN, in depth

A CDN is a globally distributed network of caching servers (edge locations, or PoPs) that sit between users and your origin. A user's request is routed - usually via anycast or DNS - to the nearest edge, which serves a cached copy if it has one and only contacts your origin on a miss. That is the whole game: turn N requests against your origin into a handful, served the rest of the time from an edge near the user. Cloudflare, Fastly, and AWS CloudFront are the common providers.

What you actually control:

  • What gets cached and for how long - via the Cache-Control header (max-age, s-maxage for the CDN specifically, immutable, stale-while-revalidate). This is the dial for freshness vs origin offload.
  • The cache key - by default the URL, but you can vary on query string, headers, or cookies. Getting this wrong either caches too little (everything misses) or serves one user's page to another.
  • Invalidation / purge - when content changes, you purge the key so the edge refetches. As always, invalidation is the hard part; content-hashed asset names sidestep it, and on-demand purges handle the rest.
  • Origin shield - a designated mid-tier edge that all other edges fetch through, so your origin sees one request per miss instead of one per edge.

A CDN also does more than cache: it terminates TLS at the edge, speaks HTTP/2 and HTTP/3 to the user, absorbs DDoS traffic and runs a WAF before requests ever reach you, and can edge-cache even dynamic HTML with short TTLs plus stale-while-revalidate. For most sites, putting a CDN in front is the highest-leverage scaling change available, static or dynamic.

The full architecture

Individually the pieces are simple; the point is how they combine. Put them together and one picture describes the whole system - the edge in front, a stateless render tier at the origin, and object storage holding the build output:

Combined frontend scaling architecture: users reach CDN edge PoPs via anycast; the edge serves cached static assets and ISR or SWR HTML, falls back to object storage on a static miss and to the origin load balancer plus stateless SSR render servers (which call backend APIs) on a dynamic or stale-HTML miss; a CI build publishes hashed immutable assets to object storage

Trace one request through it:

  1. The request is routed by anycast/DNS to the nearest CDN edge.
  2. If the edge holds the asset, or a fresh-enough cached HTML page, it answers immediately - the vast majority of traffic ends here, near the user, never reaching your servers.
  3. On a static miss, the edge pulls the file from object storage (where the CI build published hashed, immutable assets), caches it, and serves it.
  4. On a dynamic or stale-HTML miss, the edge goes to the origin: a load balancer spreads the request across a stateless, autoscaled pool of SSR render servers, which call the backend APIs for data, render the HTML, and return it - and the edge caches that result with stale-while-revalidate, so the next user is a hit.

The shape is the whole lesson: the edge absorbs the load, the origin only ever sees the misses, and the static build is just files. That is why a well-arranged frontend scales - most requests never touch the parts that cost you anything.

The scale ladder

Put together, scaling a frontend is a short, ordered climb:

  1. One server serving everything - fine until it is not.
  2. Move static assets to object storage + a CDN. Immutable, hashed, cached at the edge. This alone offloads the vast majority of traffic.
  3. Put the CDN in front of the dynamic pages too - edge-cache the HTML with s-maxage and stale-while-revalidate, and use ISR so most "dynamic" requests are served static-fast.
  4. Scale the render tier horizontally, and push SSR to the edge for the pages that must render per request.

Do those in order and a frontend stops being a scaling problem long before you run out of moves. Next in this set we go behind the CDN to the part that actually computes: scaling the backend.

More from this blog

P

Programming, System Design and AI | Latencot

53 posts

Here, you will read all about tech and tech updates. From writing code to building AI systems, we'll talk and share about everything.