DEV Community

Cover image for Website performance: 9 steps to a faster site, in the right order
Seppe Gadeyne
Seppe Gadeyne

Posted on Originally published at straffesites.com

Website performance: 9 steps to a faster site, in the right order

Most slow websites don't need another caching plugin. They need a diagnosis, one fix for the biggest delay, and a second measurement that proves the fix worked.

This is the sequence I use when I make a site faster, including my own site, straffesites.com. It's written for developers who maintain marketing sites, blogs, or content-heavy frontends. The order is deliberate: compressing a heavy hero image does more than optimizing twenty tiny icons, and fixing a slow server matters more than polishing a loading animation.

1. Measure first rather than relying on guesswork

Start with at least two kinds of measurement. A lab test such as Lighthouse or WebPageTest gives you a reproducible snapshot. Field data from the Chrome UX Report (CrUX) or your own real-user monitoring shows what visitors experience on slower devices and connections. The two don't need to match because they answer different questions. When I measured 19 website builders, the gap was bigger than I expected: lab LCP diverged from the CrUX LCP by up to 16.7x.

Don't stop at one total score. Note the Largest Contentful Paint (LCP) for the most important visible element, Interaction to Next Paint (INP) for responsiveness, and Cumulative Layout Shift (CLS) for visual stability. The thresholds come from Google: "LCP should occur within 2.5 seconds of when the page first starts loading", "INP of 200 milliseconds or less" and "CLS of 0.1" (web.dev, Core Web Vitals).

Assess the 75th percentile of each metric separately for mobile and desktop. A page passes when LCP, INP, and CLS each meet their threshold at that percentile. Averages don't count, and it doesn't mean the same 75% of page loads pass all three.

Also record the server response, total JavaScript weight, and the largest network requests. That's your baseline.

  • Test mobile and desktop on a cold cache.
  • Measure a homepage, a content page, and a conversion page separately.
  • Keep the waterfall; it shows waiting time and dependencies.
  • Repeat the same test after every meaningful change.

2. Lower the waiting time before the first HTML

If the browser waits too long for the first document, no frontend optimization can fully hide the delay. Check the Time to First Byte (TTFB). Slow database queries, heavy server-side plugins, a distant hosting region, or dynamic rendering without a cache can delay every page before the browser even starts.

For mostly static marketing pages, static generation is often the simplest win: the HTML is prepared in advance and served from a global network. For dynamic applications, targeted server caching helps, but never cache personal or shopping cart data blindly. Measure the origin directly as well, because a CDN can temporarily mask a slow source.

3. Make the main image as light as its function allows

Images often account for most of a page's weight, and the hero image is frequently the LCP element too. HTTP Archive's yearly measurement leaves little doubt: the median mobile home page weighed 2,559 KB in July 2025 (Web Almanac 2025, Page Weight). A year earlier, that median home page included 900 KB of images and 558 KB of JavaScript (Web Almanac 2024, Page Weight).

Never deliver a 3000-pixel-wide photo to a 720-pixel box. Generate responsive variants, use a modern format such as WebP or AVIF, and choose the compression level based on the image type. A soft photograph tolerates more compression than an interface image with sharp text.

Don't lazy-load the main image above the fold; the browser should discover it immediately. Images further down the page can load lazily. Always include width and height so the browser reserves space in advance and the layout doesn't shift.

<!-- Above the fold: discoverable right away, space reserved -->
<img src="/hero-1440.avif" width="1440" height="810" alt="..." fetchpriority="high">

<!-- Further down: load when needed -->
<img src="/detail-720.webp" width="720" height="405" alt="..." loading="lazy">
Enter fullscreen mode Exit fullscreen mode

No single format always wins. Choose based on the content, browser support, and whether you actually need transparency or animation.

Format Best for Strength Watch out
AVIF Photos and complex illustrations Small file at high quality Encoding takes longer
WebP General web use Broadly supported and compact Sometimes larger than AVIF
SVG Logos, icons, and diagrams Sharp at every resolution Not suitable for photos
PNG Lossless graphics and transparency Exact pixels and alpha Often unnecessarily heavy

4. Don't let critical CSS wait for the rest

The browser needs CSS to draw the first viewport correctly. One large stylesheet with rules for every page delays that moment. Remove unused rules, split the CSS where it makes sense, and avoid a chain of @import files. Keep the critical path smaller than the full component library so relevant content appears sooner.

Inlining critical CSS can help in large applications, but it also adds maintenance work. Start with a simpler setup: one well-cached stylesheet, local fonts, and no blocking external styles. Then check whether splitting it further produces a measurable gain.

5. Ship less JavaScript and run it later

JavaScript costs more than download time. The browser must parse, compile, and execute it, usually on the same main thread that also handles your visitor's input. A bundle that feels harmless on your laptop can cause a noticeable delay on an average phone.

For responsiveness, Google now measures INP instead of First Input Delay: "INP is the successor metric to First Input Delay (FID)" (web.dev, Interaction to Next Paint). Optimizing for an old FID score today means optimizing for a metric that is no longer assessed.

Remove libraries that solve one small interaction, load page-specific code only where it's needed, and use native HTML for accordions, forms, and video where possible. Add defer to non-critical scripts and initialize heavy widgets only after a deliberate interaction. The fastest code is still the code you don't ship.

6. Limit web fonts without losing your identity

Five families and twelve weights are rarely necessary. Choose one strong display family and one readable body family, subset the characters you use, and load only the weights that appear on the page. Host fonts locally when you want control over caching, privacy, and availability.

Preload only the font needed directly above the fold. Too many preloads compete with the main image and CSS. Use font-display: swap so text stays visible while the font arrives, and choose fallback fonts with similar proportions to limit shifting.

What a Lighthouse run shows on a static site

Numbers only matter when you can check them. This is the unedited Lighthouse CLI report I ran on the straffesites.com homepage on September 4, 2026, under emulated mobile conditions. The site is a static Astro build on Vercel, so treat it as one lab snapshot, not field data:

# Lighthouse 13.4.1 — https://straffesites.com/
Tested September 4, 2026 · Chrome 143 (headless) · emulation: Moto G Power, slow 4G (simulated throttling)

## Performance 100/100

| Metric | Value | Assessment |
|--------|-------|------------|
| First Contentful Paint | 1.3 s | Good |
| Largest Contentful Paint | 1.7 s | Good |
| Total Blocking Time | 0 ms | Good |
| Cumulative Layout Shift | 0.014 | Good |
| Speed Index | 1.3 s | Good |
| Time to Interactive | 1.7 s | Good |

This is a laboratory run, not CrUX field data: the scores describe how the page performs under controlled network and device conditions, not what every visitor experiences today.

## What the run confirms

- Root document served in 90 ms (server-response-time: 90 ms).
- 16 network requests, 502 KiB transferred in total (total-byte-weight).
- No render-blocking requests with savings left (render-blocking: no savings).
- Main-thread work 0.8 s, JS boot-up time 0.0 s — there is almost no JavaScript.
- CLS 0.014: image and font dimensions are reserved, nothing shifts while loading.
- The LCP element is the top case image; it is discovered and loaded without delay.

## Remaining opportunities (outside the score)

| Audit | Savings | Detail |
|-------|---------|--------|
| Improve image delivery | 272 KiB | Case images are served larger than their rendered size; WebP at the exact render size or a responsive srcset saves the most |
| Reduce unused CSS | 11 KiB | One inlined @font-face rule carries glyphs the homepage never uses |

Both opportunities are optimizations, not defects: no audit pulls the score below 100. A 100 does not mean "done forever" — every new image, script, and font can drag the next run back down. That is why we measure again every month.

---

Run environment: Lighthouse CLI 13.4.1, Node 26, headless Chrome on Linux, performance category only.
Enter fullscreen mode Exit fullscreen mode

Even a perfect lab score still lists image work. That's the point of step 3.

7. Use caching and a CDN as accelerators, not bandages

Set long cache headers for assets with unique, hashed file names. When the content changes, the hash changes and the browser automatically gets the new version. HTML usually needs a shorter cache period because current content matters more than maximum retention.

# Hashed assets: safe to cache for a year
Cache-Control: public, max-age=31536000, immutable

# HTML: always revalidate
Cache-Control: public, max-age=0, must-revalidate
Enter fullscreen mode Exit fullscreen mode

A CDN brings files closer to visitors and absorbs traffic peaks. But it doesn't make a file smaller and doesn't remove blocking code. Fix the weight and dependencies first, then distribute the efficient version.

8. Treat every external script as a performance budget

Chat widgets, heatmaps, ad pixels, video players, and tag managers often come from other servers. You don't control their speed, and one tag manager can quietly start dozens of requests. Keep an inventory of every external script with an owner and a clear purpose.

Load marketing tags only after consent. Show a local video poster first, then load the external player after a click. Remove tools that nobody uses. Treat "can this script be added?" as seriously as "can this feature be added?"

9. Make speed part of every release

A one-off optimization doesn't stay effective on its own. New images, tags, and components gradually increase the page weight. Set budgets for JavaScript, images, and Core Web Vitals, make the build warn when a budget is exceeded, and recheck the most important templates after a major change.

Review field data and segments each month: mobile versus desktop, new versus returning visitors, and slow versus fast connections. An average can hide a poor experience for mobile users. Performance work is done when it's monitored, not when a single Lighthouse run turns green.

Assign every warning to an owner. A red number without a responsible person can stay unresolved, while a ticket with the URL, the regression, and the expected budget is easier to discuss. For new components, include performance in the acceptance criteria: how much JavaScript is added, which image becomes the LCP element, and what happens on a slow connection? That turns speed from a reactive fix into a normal design decision.

Don't forget reliability. A fast first render that then freezes because of a faulty script is still a poor experience. Check console errors, failed requests, and timeouts alongside the vitals.

What to tackle first on Monday

  1. Create a baseline. Note LCP, INP, CLS, server response, and the three heaviest requests.
  2. Fix one big bottleneck. Usually the main image, server work, or blocking JavaScript.
  3. Measure the same page again. Prove the gain before starting the next optimization.

That keeps the work verifiable, and you can explain why a change got priority. You don't end up with a pile of plugins, but with a measurably faster site.


Adapted for DEV from Website performance: improve your site speed in 9 clear steps, originally published on straffesites.com.

Top comments (1)

Collapse
 
pushpendra2011 profile image
Pushpendra Yadav •

Solid ordering, especially “measure the same page again before the next fix.” One case I’d love your take on: generative/streaming UIs, where the main content is LLM text streamed in over a few seconds. LCP keeps updating as larger text blocks arrive, so the metric ends up measuring generation time rather than load performance. Do you treat the first streamed token as the real “content visible” moment and track it with a custom mark, or still optimize for LCP as reported?