Page Speed Optimization
Every millisecond matters. Research consistently shows that slower page loads lead to higher bounce rates, lower conversion rates, and reduced user satisfaction. Amazon famously found that every 100ms of added latency cost them 1% in sales. Google found that a 0.5-second delay in search page generation dropped traffic by 20%. Page speed optimization is the practice of identifying and eliminating the bottlenecks that stand between your server and a fully rendered page in the user's browser.
The Critical Rendering Path
To optimize page speed, you first need to understand how browsers turn your HTML, CSS, and JavaScript into pixels on screen. This process is called the critical rendering path, and it follows a specific sequence:
- HTML parsing — the browser downloads the HTML document and begins parsing it into a Document Object Model (DOM) tree
- CSS parsing — as the browser encounters CSS files (via
<link>tags or inline styles), it parses them into a CSS Object Model (CSSOM) - Render tree construction — the browser combines the DOM and CSSOM to determine which elements are visible and what styles apply to them
- Layout — the browser calculates the exact position and size of each element on the page
- Paint — the browser fills in pixels, drawing text, colors, images, borders, and shadows
- Compositing — separate layers are combined into the final image displayed on screen
The critical insight is that rendering is blocked until both the DOM and CSSOM are complete. Any resource that delays these steps delays the entire page.
Render-Blocking Resources
By default, CSS is render-blocking: the browser will not paint anything until it has downloaded and parsed all CSS files linked in the <head>. This is by design — rendering unstyled content would create a flash of unstyled content (FOUC) and then a jarring layout shift when styles arrive.
JavaScript can also be render-blocking when loaded synchronously. A <script> tag without async or defer attributes forces the browser to stop parsing HTML, download the script, execute it, and then resume parsing.
Strategies to mitigate render-blocking:
- Inline critical CSS — embed the CSS needed for above-the-fold content directly in the
<head>, then load the full stylesheet asynchronously - Use
deferfor scripts — this tells the browser to download the script in parallel but execute it only after HTML parsing is complete - Use
asyncfor independent scripts — the script downloads in parallel and executes as soon as it's ready, but this can cause ordering issues for scripts that depend on each other - Move scripts to the bottom of
<body>— a simpler alternative that ensures HTML is parsed before scripts execute - Use media queries on CSS links —
<link rel="stylesheet" href="print.css" media="print">tells the browser this stylesheet is only needed for printing, so it won't block rendering
Minification and Compression
Every byte you send over the network takes time. Two complementary techniques reduce the size of your resources:
Minification
Minification removes unnecessary characters from source code without changing its functionality: whitespace, comments, long variable names (in JavaScript), and redundant syntax. For CSS, tools like cssnano or clean-css can reduce file sizes by 20-40%. For JavaScript, Terser or esbuild can achieve similar or better reductions.
Compression
Compression algorithms applied at the server level further reduce transfer sizes. The two most important algorithms are:
- gzip — widely supported and typically reduces text-based files by 60-80%. Virtually every web server and CDN supports gzip out of the box.
- Brotli — developed by Google and supported by all modern browsers. Brotli achieves 15-25% better compression ratios than gzip for text-based assets like HTML, CSS, and JavaScript. It is especially effective for static assets that can be pre-compressed at a higher compression level.
Content-Encoding response header to verify compression is active.
Code Splitting and Tree Shaking
Code splitting breaks your JavaScript bundle into smaller chunks that load on demand rather than forcing users to download everything upfront. Modern bundlers like Webpack, Rollup, and Vite support code splitting through dynamic import() statements. The most common strategy is route-based splitting, where each page or route gets its own chunk.
Tree shaking is the process of eliminating dead code — functions, classes, and modules that are imported but never actually used. This requires ES module syntax (import/export) because the static structure of ES modules allows bundlers to analyze the dependency graph at build time. CommonJS (require) is dynamic and cannot be reliably tree-shaken.
Together, code splitting and tree shaking can dramatically reduce the amount of JavaScript a user needs to download for any given page, directly improving both LCP and INP.
Reducing HTTP Requests
Each HTTP request adds overhead: DNS lookups, TCP connections, TLS handshakes, and round-trip latency. While HTTP/2 and HTTP/3 have reduced the cost of individual requests through multiplexing, fewer requests still means faster pages.
- Combine small CSS files into a single stylesheet where practical
- Use CSS sprites or icon fonts for small, frequently used images (though inline SVG is often a better modern approach)
- Inline small assets like tiny images (as data URIs) or critical CSS directly into the HTML
- Remove unnecessary third-party scripts — analytics tags, chat widgets, and social media embeds each add multiple requests and can significantly slow your page
Optimizing Web Fonts
Custom fonts improve design but can hurt performance. A poorly loaded font causes either invisible text (FOIT — Flash of Invisible Text) or a jarring text reflow (FOUT — Flash of Unstyled Text) when the font arrives.
font-display: swap— tells the browser to immediately render text with a fallback font, then swap in the custom font when it's ready. This prevents invisible text and ensures content is readable immediately.- Preload critical fonts —
<link rel="preload" href="font.woff2" as="font" type="font/woff2" crossorigin>tells the browser to start downloading the font early, before CSS parsing discovers it. - Use WOFF2 format — WOFF2 offers significantly better compression than WOFF or TTF. All modern browsers support it.
- Subset fonts — if you only use Latin characters, create a subset that excludes Cyrillic, Greek, and other character sets. Google Fonts does this automatically with the
textparameter. - Limit font variations — each weight and style is a separate file. Use only the weights you actually need (e.g., 400 and 700, not 100 through 900).
Server Response Time (TTFB)
Time to First Byte (TTFB) measures how long it takes from the browser's request to receiving the first byte of the server's response. A slow TTFB delays everything — no rendering can begin until the HTML arrives. Google recommends a TTFB under 800ms, but aim for under 200ms for the best user experience.
Common causes of slow TTFB and their solutions:
- Slow database queries — optimize queries, add indexes, implement query caching
- Server-side processing — cache rendered HTML, use output buffering, optimize application code
- Geographic distance — use a CDN to serve content from edge locations closer to users
- Resource contention — ensure your server has adequate CPU, memory, and I/O capacity for your traffic levels
- Cold starts — in serverless environments, pre-warm functions or use provisioned concurrency
Content Delivery Networks (CDNs)
A CDN caches your content at edge servers distributed across the globe. When a user requests your page, the CDN serves it from the nearest edge location rather than routing the request back to your origin server. This reduces latency, improves TTFB, and offloads traffic from your origin.
Modern CDNs like Cloudflare, Fastly, and AWS CloudFront also provide:
- Automatic Brotli and gzip compression
- Image optimization and format conversion
- HTTP/2 and HTTP/3 support
- DDoS protection and Web Application Firewall (WAF)
- Edge computing capabilities for dynamic content
Putting It All Together
Page speed optimization is not a one-time task — it's an ongoing discipline. Start with the highest-impact changes: enable compression, add a CDN, defer non-critical JavaScript, and optimize your largest images. Then use tools like Lighthouse, WebPageTest, and PageSpeed Insights to identify the next bottleneck. The goal is not a perfect score but a consistently fast experience for your real users.
Resources
- Google PageSpeed Insights — analyze any URL with both lab and field performance data
- WebPageTest — detailed waterfall charts, filmstrip views, and multi-location testing
- Critical Rendering Path — Google's deep dive into how browsers render pages
- Code Splitting Guide — practical guide to splitting JavaScript bundles