Elementary Standards
HTMLCSSWeb StandardsSEOBlogStart reading
Home / Blog / Page Speed Optimization: Techniques That Actually Work
Performance

Page Speed Optimization: Techniques That Actually Work

Most "speed optimization" checklists optimize for the wrong metric. Here's what actually moves LCP, INP, and CLS in production.

TL;DR: Most page-speed checklists optimize total page weight, but weight isn't what users or Core Web Vitals measure. What actually moves LCP, INP, and CLS is the critical rendering path: what blocks the main thread, what delays the largest visible element, and what shifts layout after paint. Fix those three, and the weight stops mattering as much.

Here's the part most checklists skip: a 4MB page can load faster than a 400KB one if the 400KB page ships a render-blocking stylesheet before its hero image, and the 4MB page preloads its LCP element and defers everything else. Byte count correlates with speed, but it isn't the mechanism — the mechanism is what the browser is forced to wait for before it can paint and respond to input. Teams that build with a web development team at YuSMP tend to bake this into the architecture from day one, which is why they rarely end up chasing performance regressions after launch — speed was a build decision, not a cleanup task.

timeline — before optimizationTTFB 0.6sLCP 4.1sload 5.2stimeline — after optimizationTTFB 0.3sLCP 1.6sload 2.1ssame assets, reordered critical path — LCP cut by more than half

What Actually Slows Down a Page in 2026?

It's rarely total page weight. The browser can download assets in parallel; what it can't do is skip steps in the critical rendering path. A page gets slow when the browser has to wait: for a render-blocking stylesheet before it can paint anything, for a long JavaScript task that locks the main thread and ignores input, for an image the browser only discovers after everything above it has parsed, or for a resource it needed from the start but was never told to prioritize. Fix the waiting, and the weight becomes a secondary concern.

How Do You Measure Page Speed Correctly?

Start with field data, not a single lab run. PageSpeed Insights and Google Search Console pull from the Chrome User Experience Report (CrUX) — real visitors, real devices, real networks over the past 28 days. Lighthouse and WebPageTest give you lab data: a single simulated run, useful for debugging a specific fix before it ships, but not a substitute for what your actual traffic experienced. A page can pass in the lab and fail in the field if your lab run skips the slow phone on a congested network that a meaningful slice of your visitors use.

The three metrics that matter are Largest Contentful Paint (LCP), Interaction to Next Paint (INP), and Cumulative Layout Shift (CLS) — the full breakdown of what moved in the 2026 measurement update and the current pass/fail thresholds lives in our Core Web Vitals deep dive; this piece focuses on the techniques that move those numbers.

Image Optimization That Actually Matters

Format matters more than most teams assume: AVIF beats WebP on compression for most photographic content, and WebP beats JPEG across the board, but browser support and encoding cost vary, so serve a fallback chain rather than betting on one format.

<picture>
  <source srcset="hero-800.avif 800w, hero-1600.avif 1600w" type="image/avif">
  <source srcset="hero-800.webp 800w, hero-1600.webp 1600w" type="image/webp">
  <img src="hero-800.jpg" srcset="hero-800.jpg 800w, hero-1600.jpg 1600w"
       sizes="(max-width: 600px) 100vw, 800px"
       alt="Descriptive text" width="1600" height="900"
       fetchpriority="high">
</picture>

The detail teams get backwards: loading="lazy" on the image that's actually your LCP element. Lazy-loading tells the browser to deprioritize a fetch until the image nears the viewport — correct for images below the fold, actively harmful for the hero image the browser needs to paint first. Use fetchpriority="high" on the LCP candidate instead, and reserve loading="lazy" for everything the user has to scroll to reach.

Cutting Render-Blocking CSS and JavaScript

A <link rel="stylesheet"> in the head blocks rendering until it downloads and parses, full stop — browsers won't paint a partially-styled page. The fix isn't removing CSS, it's splitting it: inline the critical CSS needed for the above-the-fold view directly in the head, and load the rest asynchronously once the page is interactive.

<style>/* critical, above-the-fold rules only */</style>
<link rel="preload" href="/assets/main.css" as="style"
      onload="this.onload=null;this.rel='stylesheet'">
<script src="/assets/app.js" defer></script>

defer and async solve different problems: defer downloads in parallel and runs after the DOM is parsed, in document order — the right default for most app scripts. async downloads in parallel and runs the instant it's ready, out of order — fine for independent third-party scripts, risky for anything your other code depends on. Minification and code-splitting (shipping only the JavaScript a route needs, not the whole bundle) compound both: less to parse, less to block on.

Resource Hints — Preload, Preconnect, Prefetch

These three get used interchangeably and shouldn't be. preload tells the browser "fetch this now, I'll need it this render" — use it for the LCP image or a critical web font. preconnect opens the DNS, TCP, and TLS handshake to a third-party origin before you request anything from it — use it for a CDN or API host you know you'll hit. prefetch is the lowest priority of the three: fetch this in idle time because the user will probably need it next, like the stylesheet for a page one click away.

<link rel="preload" as="image" href="hero.avif" fetchpriority="high">
<link rel="preconnect" href="https://api.example.com">
<link rel="prefetch" href="/next-page.html">

Misusing preload on things the browser doesn't end up needing wastes bandwidth that the actual LCP resource could have used — it's a scarce-priority signal, not a general-purpose speed boost.

Caching and CDN Strategy

Static assets with a content hash in the filename (app.3f9a1c.js) can be cached essentially forever, since a new deploy produces a new filename:

Cache-Control: public, max-age=31536000, immutable

HTML documents need shorter or no caching, since they're the thing that changes on every deploy. A CDN sitting in front of your origin serves cached static assets from an edge node near the visitor, cutting the network round-trip that a single-region origin can't avoid — this helps even single-region audiences, since it still removes TLS handshake latency and origin load from every request.

Is HTTP/3 Worth Switching To?

HTTP/2 already solved head-of-line blocking at the HTTP layer by multiplexing multiple requests over one TCP connection. HTTP/3 goes further by replacing TCP with QUIC, which multiplexes over UDP — so a single lost packet no longer stalls every other in-flight request on the connection, which is exactly what happens with HTTP/2 over TCP. The practical gap between the two is small on a stable broadband connection and meaningfully larger on lossy mobile networks, where packet loss is routine. If a large share of your traffic is mobile on variable connections, HTTP/3 is worth the switch; if your audience is almost entirely wired desktop, it's a smaller win than the other items on this list.

Rendering Strategy — SSR, SSG, or Client-Side?

Rendering strategy sets the floor for TTFB and LCP before any other optimization runs. A pure client-side rendered (CSR) app ships a near-empty HTML shell, then downloads, parses, and executes JavaScript before anything meaningful paints — every optimization above still has to wait behind that. Server-side rendering (SSR) sends real HTML on the first response, so the browser can paint content while JavaScript hydrates in the background; static site generation (SSG) pre-renders at build time, so there's no server computation at all on the request path, just a static file served from the edge. For most content-driven pages, SSG wins on raw speed; for pages with per-request personalization, SSR is the better trade. A mobile-first approach to layout compounds whichever rendering strategy you pick, since mobile devices have the least main-thread headroom to absorb a slow hydration step.

TechniqueWhat It ImprovesWhen to Use It
AVIF/WebP with responsive srcsetLCP, total transfer sizeAny image-heavy page
fetchpriority="high" on LCP imageLCPThe single largest above-the-fold element
Critical CSS inline + async restLCP, render-blocking delayAny page with a non-trivial stylesheet
defer / code-splitting JSINP, main-thread availabilityApp scripts not needed for first paint
preload / preconnectLCP, TTFB to third-party originsKnown-critical resources and external hosts
Immutable cache headers on hashed assetsRepeat-visit load timeStatic JS/CSS/image files
CDN edge cachingTTFB, network latencyAny public site with a geographically spread audience
HTTP/3 (QUIC)Resilience to packet lossAudiences heavy on mobile/lossy networks
SSR or SSG over pure CSRTTFB, LCP, time-to-first-paintContent and personalized pages alike
Reserved box dimensions on images/embedsCLSAny asynchronously-loaded visual element

Common Page Speed Mistakes Worth Fixing First

Frequently Asked Questions

Does page speed directly affect SEO rankings?

Core Web Vitals are part of Google's page experience signal, which is one of many ranking factors — a fast page doesn't guarantee a top position, but a page that fails LCP, INP, or CLS badly enough can be held back regardless of how good its content is.

What's a good Lighthouse performance score?

90 and above is generally considered good, but treat it as a lab diagnostic, not the pass/fail metric — field data from CrUX and Search Console is what's actually weighed for the page experience signal.

Should I lazy-load every image?

No. Lazy-load every image below the fold; never lazy-load the image the browser needs to paint first, since that's almost always your LCP element and lazy-loading it directly delays the metric.

Does a CDN help if my audience is in one region?

Yes, though less dramatically. Even with a single-region audience, a CDN removes TLS handshake overhead and origin load from every request and can serve cached static assets faster than a single origin server under load.

Is HTTP/3 enough on its own to fix slow pages?

No — it reduces the impact of packet loss on a connection, but it doesn't fix a render-blocking stylesheet, an unoptimized image, or a long JavaScript task. Treat it as one layer, not a substitute for the others.

How often should I re-test page speed after a fix?

Re-run lab data (Lighthouse/PageSpeed Insights) immediately after deploying to confirm the fix landed, then check field data in CrUX or Search Console after it accumulates roughly 28 days of real traffic, since that's the window the field-data pass/fail actually reflects.

None of these techniques work in isolation forever — a fast hero image behind a render-blocking script is still a slow page, and a well-tuned critical path still loses to a slow origin server. Treat page speed as an ordering problem first: get the browser painting the right thing as early as possible, then trim everything that still makes it wait.

Back to the blog
More notes

Keep reading