SSR vs SSG: Server-Side Rendering vs Static Generation
SSR renders pages per request; SSG renders them at build time. How the tradeoff affects speed, freshness, and hosting cost — and how to pick.
Server-side rendering (SSR) generates a page’s HTML on the server for every incoming request. Static site generation (SSG) generates that HTML once, at build time, and serves the identical file to every visitor afterward. Both produce full HTML before it reaches the browser — the difference is when that rendering happens, and that single distinction cascades into very different tradeoffs around speed, cost, and how fresh the content can be.
What both are reacting to
Client-side rendering — where the server ships a mostly empty HTML shell and JavaScript builds the page in the browser — has a well-known cost: the user stares at a blank or skeleton page until the JavaScript bundle downloads, parses, and executes. SSR and SSG both exist to avoid that by sending real, visible content in the first response. The browser still needs to attach interactivity afterward — a process called hydration — but the page is legible and often usable before that finishes.
Server-side rendering
With SSR, every request hits the server, which runs the rendering logic — often fetching data from a database or API — and returns fresh HTML. Because this happens per request, SSR pages can reflect real-time data: a logged-in user’s dashboard, live inventory counts, personalized content.
The cost is that every request does real work. Rendering takes server CPU time and, if it needs data, a round trip to a database or API before the response can even start. That per-request cost is why SSR pages typically need application servers running continuously, plus caching layers to keep response times acceptable under load — see what a load balancer does to spread that load, and what a reverse proxy does to sit in front of it and often cache responses.
Static site generation
With SSG, the entire site — or every page whose content is knowable ahead of time — is rendered once during a build step, producing plain HTML files. Those files get uploaded to a CDN and served directly, with no server-side rendering work happening per request at all.
This is about as fast as serving a page can get: no database query, no template rendering, no backend logic in the request path — just a static file coming from an edge location close to the visitor. It’s also cheap to host and scales to enormous traffic without extra infrastructure, since a CDN serving static files doesn’t care whether it’s handling ten requests a second or ten thousand.
The tradeoff is freshness. If the underlying content changes, the static HTML doesn’t know until the site is rebuilt and redeployed. That’s fine for a blog post or documentation page that changes occasionally, and it’s exactly the model this site itself uses — see deploying an Astro site to Cloudflare Pages for a concrete build-and-deploy pipeline. It’s a poor fit for a page that needs to show something different for every visitor or every second.
Head-to-head
| SSR | SSG | |
|---|---|---|
| When rendering happens | Per request | At build time |
| Time to first byte | Depends on server + data fetch | Near-instant (served from CDN) |
| Content freshness | Always current | Fixed until next build/deploy |
| Personalization | Native | Not possible without client-side fetching |
| Server infrastructure | Requires a running application server | Static file hosting is enough |
| Scaling cost | Grows with traffic and render cost | Flat — a CDN handles scale |
| Best for | Dashboards, feeds, personalized or real-time pages | Blogs, docs, marketing pages, product pages that don’t change per visitor |
The hybrid middle ground
Most real frameworks don’t force an all-or-nothing choice anymore. Incremental static regeneration and on-demand revalidation let a page start out statically generated but get rebuilt in the background after a set time or when triggered by a content change — combining SSG’s speed with something closer to SSR’s freshness, without paying the per-request rendering cost on every single visit.
Another common pattern is rendering the page’s shell statically while fetching personalized or frequently changing pieces client-side after load — effectively mixing SSG for the parts that don’t change per visitor with a lightweight client fetch for the parts that do. This is related to, but distinct from, React Server Components, which change where component logic runs rather than when — it’s a separate axis from the build-time-vs-request-time question SSR and SSG answer.
How to choose
A few questions usually settle it:
- Does the content differ per visitor? If yes — a dashboard, an account page, search results — SSR (or client-side fetching) is required. SSG physically can’t produce per-visitor HTML at build time.
- How often does the content change, and does staleness matter? A changelog that updates weekly is a great SSG candidate. A stock ticker is not.
- What’s the traffic pattern? High, spiky, or unpredictable traffic favors SSG’s flat CDN-serving cost over SSR’s per-request server load — this is a big part of what Core Web Vitals scoring rewards, since a cached static response reliably beats a server render on time-to-first-byte.
- What’s the operational budget? SSG needs a CDN and a build pipeline. SSR needs a running server (or serverless functions) that scales with traffic, plus the monitoring and capacity planning that comes with it.
The takeaway
SSR and SSG both send fully-formed HTML to the browser instead of an empty shell — the difference is whether that HTML is built fresh on every request or once ahead of time. SSG wins on speed, cost, and simplicity whenever content doesn’t need to differ per visitor or update in real time; SSR is the right call when it does. Most production sites end up mixing both, statically generating what doesn’t change per request and rendering on the server (or fetching client-side) what does.
Keep reading
Takina · · 4 min read What Is the Beacon API? navigator.sendBeacon()
The Beacon API lets a page send one last async request as it unloads, without blocking navigation or racing the browser's page teardown.
Takina · · 3 min read What Is Fetch Priority? The fetchpriority Attribute
fetchpriority lets you tell the browser which resources matter most, overriding its default heuristics to load critical assets sooner.
Takina · · 5 min read CSS will-change Explained: Compositing and Performance
The CSS will-change property hints the browser to prepare an element for an upcoming change, moving it to its own compositor layer. When to use it and when not to.