Web Performance: Compression, Caching, SSR/CSR/SSG
Show the page quickly on the user's phone: send fewer bytes (Gzip/Brotli), do not send again (Cache-Control/ETag), load only when needed (lazy) and choose the right rendering.
52.1 Why is it used?
- Every extra 100ms of load time lowers conversion; in e-commerce a slow page means an abandoned cart.
- Many users in India are on slow 4G/budget phones; heavy JS and big images make the page slow for them.
- Compression and browser caching reduce both the bandwidth bill and server load.
- Core Web Vitals (LCP, INP, CLS) count in Google search ranking; performance matters for SEO.
- On sale day, if static files come from the browser/CDN cache, the load on the origin server drops a lot.
52.2 How it works
- Gzip: older text compression supported everywhere (HTML/CSS/JS/JSON); cuts size by roughly 60-80%; header Content-Encoding: gzip.
- Brotli: Google's newer algorithm, producing roughly 15-25% smaller output than Gzip on text files; supported over HTTPS, header Content-Encoding: br.
- Cache-Control: max-age=31536000, immutable for hashed static files (app.3f9a.js); no-cache for HTML (revalidate every time); no-store for sensitive data.
- ETag / Last-Modified: the server sends a version tag; the browser sends If-None-Match, and if the file is the same it gets 304 Not Modified; the body is not sent again.
- Lazy Loading: images/components lower down load only when they come onto the screen: img loading="lazy", dynamic import(), code splitting.
- CSR (Client-Side Rendering): empty HTML + a big JS bundle, the page is built in the browser; fine for interactive apps, but weak first load and SEO.
- SSR (Server-Side Rendering): the server builds and sends HTML on every request, then hydration; fast first paint, good SEO, higher server cost.
- SSG / ISR: HTML is generated at build time and served from the CDN; the fastest; with ISR (Next.js) the page regenerates in the background every N seconds.
- 1.Browser (Chrome, slow 4G): 1 GET /static/app.3f9a.js
- 2.CDN edge (Cloudflare / CloudFront) + Nginx origin (gzip on; brotli; ETag): 2 200 OK, Content-Encoding: br, 2 MB -> ~500 KB, Cache-Control: max-age=31536000, immutable
- 3.Browser cache: app.3f9a.js, CSS; repeat visit = cache hit, 0 network
- 4.HTML revalidation: 3 GET /index.html If-None-Match: "v7" -> 4 304 Not Modified, no body, only headers
Hashed JS/CSS = immutable for 1 year; HTML = no-cache + ETag; sensitive = no-store.
- 1.Lazy loading: on the first screen (above the fold) the Hero banner loads instantly as WebP, and so do Price + Buy; images further down use img loading="lazy"; the Reviews widget uses dynamic import and loads only when scrolled to
- 2.CSR: empty HTML + 2 MB JS -> JS runs, API call -> page built in the browser; slow first load, weak SEO; best: dashboard, admin panel
- 3.SSR (Next.js): HTML built on the server on every request -> ready HTML, fast paint -> hydration, interactive; good SEO, higher server cost; best: product page with live price/stock
- 4.SSG / ISR: HTML at build time -> ready HTML from the CDN, revalidate: 60s; fastest, low server load; best: sale landing, blog, category
Result (WebPageTest, Mumbai 4G): LCP 6s -> ~2s; JS 2 MB -> 500 KB; very little traffic on the origin.
52.3 Tools
| Tool | Type | How it is used |
|---|---|---|
| Next.js | React framework | In one app you can choose per-page SSR, SSG, ISR (revalidate: 60) and CSR; next/image gives automatic lazy loading and resizing. |
| Nginx | Web server | Rules such as gzip on; the brotli module; location /static { expires 1y; add_header Cache-Control "public, immutable"; }. |
| Lighthouse | Audit tool | Built into Chrome DevTools; gives a performance score, LCP/CLS/TBT and suggestions (unused JS, uncompressed text). |
| WebPageTest | Testing tool | Tests from real devices and locations (Mumbai, slow 4G) and shows what is slow with a waterfall chart and filmstrip. |
| Cloudflare / CloudFront | CDN | Brotli compression, cache rules and image optimization (resize, WebP/AVIF) at the edge, automatically. |
| Webpack / Vite | Bundler | Do code splitting, tree shaking, minification and hashed filenames so that long caching is safe. |
52.4 Comparison: CSR vs SSR vs SSG (ISR)
| Point | CSR | SSR | SSG / ISR |
|---|---|---|---|
| When the HTML is built | In the browser, after JS runs | On the server, on every request | At build time (periodically with ISR) |
| First load speed | Slow: JS must download first | Fast first paint | Fastest: ready HTML from the CDN |
| SEO | Weak (the crawler has to run JS) | Good | Very good |
| Server load | Low: only static files | High: renders on every request | Very low: the CDN serves it |
| Data freshness | Real-time (API call) | Real-time per request | Stale until the build or revalidate time |
| Personalization | Easy | Easy (cookie based) | Hard: has to be added client side |
| Best for | Dashboards, admin panels, logged-in apps | Product page with live price/stock, news | Blogs, docs, landing pages, category pages |
52.5 When to choose which
- Brotli for text (over HTTPS), Gzip as a fallback; do not compress images again: use WebP/AVIF.
- Hashed static files: Cache-Control max-age=1 year + immutable; HTML: no-cache + ETag.
- SSG/ISR: when content rarely changes and you need SEO: marketing pages, blogs, category listings.
- SSR: when you need SEO and the data must be fresh on every request: a product page with price/stock.
- CSR: dashboards after login where SEO is not needed.
52.6 Real-world example
An illustrative scenario: optimizing an e-commerce sale landing page.
- Lighthouse report: LCP 6s, JS bundle 2 MB, uncompressed JPEG images, no caching headers.
- Brotli is enabled on Nginx/CDN; the 2 MB JS becomes roughly 500 KB in transfer.
- Hashed filenames from the bundler + Cache-Control max-age=1 year; on a repeat visit JS/CSS come from the browser cache, 0 network.
- HTML gets no-cache + ETag: it is checked every time, and if unchanged the answer is 304 Not Modified (headers only).
- Banner images in WebP, product images further down with loading=lazy; the first screen appears quickly.
- The landing page is served from the CDN via SSG + ISR (revalidate 60s); the product page uses SSR so price/stock stay live.
- Retest on WebPageTest (Mumbai, 4G): LCP from 6s to roughly 2s; very little traffic on the origin server.
52.7 Interview tips
- no-cache does not mean 'do not cache', it means 'revalidate before using'; no-store means do not store at all.
- Common mistake: putting a long max-age on index.html; a new deploy will not reach users, so HTML should always have a short cache or revalidate.
- Running gzip again on already compressed files (JPEG, MP4, ZIP) wastes CPU and does not reduce size.
- Lazy loading even the above-the-fold hero image makes LCP worse. Numbers to quote: Brotli 2 MB -> ~500 KB, LCP 6s -> ~2s.