Web Performance: Everything That Matters in 2026
The complete guide to making websites fast — how browsers load pages, Core Web Vitals, images, JavaScript, CSS, servers, third-party scripts, measurement, budgets, and why Astro wins by default.
This is the guide we hand to clients who want to understand why their site is slow and what to do about it. It is long because performance is a stack of decisions, not one trick, and any guide that tells you otherwise is selling you something. We are going to walk the whole stack — from the moment a user types your URL to the moment the page is usable — and explain where the seconds go and how to get them back.
Everything here is backed by numbers we have measured on real projects. None of it requires expensive tools. By the end you will know exactly what to fix first and why.
Why performance matters
Let’s start with the business case, because the technical case is meaningless if you do not care about the outcome.
Bounce rate. Google’s own research: as page load time moves from one second to three seconds, bounce probability rises 32%. At five seconds, more than half of mobile visitors have left. At ten seconds, almost everyone is gone and the few who remain are frustrated. The curve is brutal and it happens in the first three seconds.
Conversion. Walmart found that every one-second improvement in load time increased conversions by roughly 2%. Deloitte’s Milliseconds Make Millions study found that a 0.1-second improvement in mobile load time lifted retail conversion by over 8%. These are not rounding errors. Speed moves revenue.
Search ranking. Google uses Core Web Vitals as a ranking signal. A slow site ranks lower than an equivalent fast site. This is not speculation — Google has stated it plainly. Speed is now both a conversion factor and a visibility factor.
Cost. Faster pages cost less to serve. Smaller bundles mean less bandwidth. Fewer requests mean lower server load. Performance is one of the rare optimizations that improves the user experience and the operating cost at the same time.
If you remember one thing from this section: on mobile, most of your audience is gone by the three-second mark. Everything else in this guide is about getting under it.
Performance on mobile is a different sport
The phone in your customer’s pocket is not the laptop on your desk, and treating them as the same is the single most common performance mistake we see. Two gaps matter.
CPU and memory. A mid-range Android — the device most of your visitors actually carry — has a CPU roughly a third the speed of a modern MacBook, and far less memory. Parsing and executing JavaScript is dramatically slower. A 200KB bundle that runs in 200 milliseconds on your laptop can take a full second on that phone. The work is identical. The hardware is not.
Network. Mobile connections are unstable. A user on cellular might have great bandwidth one second and terrible latency the next, especially indoors, in transit, or in a crowded area. Round trips that cost nothing on fiber cost real time on 4G, and every additional request your page makes is another chance to hit a bad moment.
Google understands this, which is why Lighthouse and PageSpeed Insights test on a simulated Moto G Power over a throttled slow 4G connection by default. That is not pessimism. It is realism. If your site passes that test, it works for the median mobile user. If you have only ever tested on your laptop, you are looking at the optimistic version of your numbers, and the gap between optimistic and real is where your bounce rate lives.
The practical implication: optimize for the throttled phone, not the desktop. Every kilobyte you cut matters more on mobile than on desktop, because the cost of that kilobyte is paid in slower CPU time and unreliable network. Ship less, and you win on every device, but you win most on the device most of your audience is using.
How browsers actually load pages
To optimize loading, you need to understand loading. Let’s trace what happens between “user hits enter” and “page is usable.” Each step is a place time can be wasted.
DNS lookup. The browser turns your domain name into an IP address. Usually tens of milliseconds, but a cold lookup on a slow network can take longer. Using a fast DNS provider and a short TTL helps.
TCP handshake. The browser opens a connection to the server. A round trip. With HTTP/3 and QUIC, this can combine with the next step.
TLS negotiation. For HTTPS, the browser and server negotiate an encrypted connection. Another round trip or two. Modern TLS 1.3 cuts this to a single round trip, which is why you want it.
Server response (time to first byte). The server receives the request and starts sending HTML. For a static file on a CDN, this is milliseconds from a nearby edge node. For a server-rendered app hitting a database, this can be a full second.
HTML download and parse. The browser downloads the HTML and starts parsing it. As it parses, it discovers references to CSS, JavaScript, fonts, and images, and queues requests for each.
CSS download and parse. CSS is render-blocking. The browser will not paint anything until it has downloaded and parsed the CSS the page needs. Large CSS files delay the first paint directly.
JavaScript download, parse, and execute. JavaScript is the expensive one. The browser downloads it, parses it into an executable form, and runs it. On a mid-range phone, parsing and executing 200KB of JavaScript can take a full second or more. This is where most modern sites lose their time.
Render. The browser constructs the DOM and CSSOM, combines them into a render tree, lays out the page, and paints pixels. This is when the user finally sees something.
Hydration (for framework sites). If you are using React, Vue, or similar, the framework then walks the page and attaches event listeners to make it interactive. Until hydration finishes, buttons may render but not respond.
The lesson: a page is not “loaded” when the spinner stops. It is loaded when the main content is painted and the page responds to input. A surprising amount of time hides in the last two steps, which is why a site can finish downloading and still feel broken.
Core Web Vitals: a deep dive
Google distills the experience into three metrics. Get these in the green and you are ahead of most of the web.
LCP — Largest Contentful Paint
The time until the largest visible element finishes painting. Usually a hero image or a large headline. Target: under 2.5 seconds on the 75th percentile of real user loads. Above 4 seconds is poor.
Common causes and fixes:
- Large unoptimized images. The number one cause. Serve WebP or AVIF, resize to actual display dimensions, and preload the hero. We have seen LCP drop from 4 seconds to under 1 second just by compressing one image.
- Render-blocking resources. CSS and synchronous JavaScript in the head. Defer non-critical scripts, inline critical CSS, and preload the most important stylesheet.
- Slow server response. If your time to first byte is 800ms, nothing downstream can be fast. Serve static files from a CDN edge.
CLS — Cumulative Layout Shift
How much the page moves while loading. Target: under 0.1. Above 0.25 is poor. CLS is the metric users feel as “janky.”
Common causes and fixes:
- Images without dimensions. Always set width and height, or an aspect-ratio in CSS, so the browser reserves space before the image loads.
- Late-loading embeds and ads. Reserve fixed space for them, or load them below the fold.
- Web font swap. The fallback font and the web font have different metrics, so text reflows when the font loads. Use
font-display: swapand size your fallback font to match the web font’s metrics.
INP — Interaction to Next Paint
The time between a user interaction (tap, click, keypress) and the next painted frame, measured across the whole page lifecycle. Replaced the old FID metric in 2024. Target: under 200 milliseconds. Above 500 milliseconds is poor.
Common causes and fixes:
- Long tasks on the main thread. Anything blocking the main thread for more than 50 milliseconds delays interaction. Break long work into smaller chunks, defer non-critical work, and move heavy computation off the main thread with web workers.
- Third-party scripts. Analytics, chat widgets, ads — each runs on your main thread. Audit and delay them.
- Heavy hydration. A large client-rendered tree keeps the main thread busy. Ship less JavaScript. Hydrate less.

Image optimization
Images are usually the heaviest things on a page and the most common cause of bad LCP. Get images right and you fix half your performance problems.
Formats. Use modern formats. AVIF gives the best compression, supported in all modern browsers. WebP is the safe fallback with near-universal support. Both can be 30–50% smaller than JPEG or PNG at the same visual quality. Serve AVIF with a WebP or JPEG fallback using the <picture> element.
<picture>
<source srcset="/img/hero.avif" type="image/avif">
<source srcset="/img/hero.webp" type="image/webp">
<img src="/img/hero.jpg" width="1600" height="900" alt="A landscape hero image served as the primary above-the-fold visual">
</picture>
Responsive images. Serve different image sizes to different screens. A phone does not need the 4000-pixel-wide image you serve a desktop. Use the srcset attribute to let the browser pick.
<img srcset="/img/hero-800.webp 800w,
/img/hero-1200.webp 1200w,
/img/hero-1600.webp 1600w"
sizes="(max-width: 600px) 800px, 1600px"
src="/img/hero-1600.webp" width="1600" height="900" alt="A responsive hero image with multiple srcset variants for different screen sizes">
Always set width and height. This lets the browser reserve the correct space before the image downloads, eliminating layout shift. It is the single easiest CLS fix there is.
Lazy-load below the fold. Add loading="lazy" to images that are not visible on initial paint, so the browser does not fetch them until they are needed. Do not lazy-load your hero image — that hurts LCP.
CDN and compression. Serve images from a CDN that can transform and compress them on the fly. Cloudflare Images, Cloudflare R2, and similar services handle format negotiation and resizing for you.
JavaScript optimization
JavaScript is where most modern sites hemorrhage time. The goal is simple: ship less of it, and ship it later.
Defer and async. JavaScript in the head blocks parsing. Use defer on scripts that can run after the document is parsed, and async on independent scripts that can run whenever they load. Almost every script on a marketing site should be deferred.
<script src="/js/app.js" defer></script>
<script src="/js/analytics.js" async></script>
Code splitting. Only load the JavaScript the current page needs. Modern bundlers can split your code into per-route or per-component chunks that load on demand. A visitor on your homepage should not download the code for your dashboard.
Tree shaking. Configure your bundler to drop unused code. If you import a library but use one function, you should ship one function, not the whole library. Most modern bundlers do this by default if you write your imports correctly.
Hydration and islands. This is the big one for framework sites. Traditional hydration ships the entire framework runtime and re-executes the whole page on the client. Astro’s islands architecture hydrates only the specific components marked as interactive, leaving the rest of the page as static HTML. We will come back to this.
Audit what you ship. Bundle analyzers show you exactly what is in your JavaScript bundle. You will usually find a heavy library you forgot about, a polyfill you no longer need, or a dependency pulling in ten times its own size. Visibility leads to cuts.

CSS optimization
CSS is less expensive than JavaScript, but it is render-blocking, so it directly delays the first paint.
Critical CSS. Inline the CSS needed to render the above-the-fold content directly in the head, and load the rest asynchronously. The browser can paint the visible part of the page without waiting for the full stylesheet.
Remove unused CSS. Many sites ship CSS for components that do not appear on the current page. Tailwind and similar utility-first systems help because they only generate the classes you actually use. PurgeCSS does the same for traditional setups.
Avoid @import. CSS @import statements are render-blocking and force the browser into serial requests. Use <link rel="stylesheet"> tags instead, which can be parallelized.
Font loading. Fonts are a common source of both slow paint and layout shift.
@font-face {
font-family: 'Body';
src: url('/fonts/body.woff2') format('woff2');
font-display: swap;
font-weight: 400;
}
font-display: swap tells the browser to show text in a fallback font immediately and swap the web font in when it loads. Preload your most important font file so it starts downloading early. Serve WOFF2 — it is the smallest format with universal modern support. Subset your fonts to the characters you actually use; a font with 200 glyphs is far smaller than one with 2,000.
Server and CDN optimization
Your server is the foundation of your time to first byte, and first byte sets the floor for everything else.
Edge rendering and static files. The fastest possible server response is a static file served from a CDN node geographically close to the visitor. This is what a static Astro site does. The file lives on hundreds of edge nodes worldwide, and the visitor gets it from whichever is nearest. Response times in the low tens of milliseconds.
Cache headers. Tell browsers and CDNs how long to cache each file. Static assets with hashed filenames (the build changes the hash when content changes) can be cached for a year. HTML should be cached more cautiously. Good cache headers mean returning visitors load your site almost instantly from their own browser cache.
HTTP/3. The latest HTTP protocol runs over QUIC and reduces connection setup time, especially on lossy mobile networks. Most modern CDNs and browsers support it. Turn it on.
Brotli compression. Compress text responses with Brotli instead of gzip. It produces smaller files, especially for text-heavy HTML, CSS, and JavaScript. Supported everywhere modern. Enable it at your CDN or server.
Server response time. If you must render on the server, keep it fast. Cache database queries. Avoid rendering blocking on external API calls. Aim for a time to first byte under 200 milliseconds. Anything over 600 milliseconds is dragging your LCP down.
Here is a concrete comparison we measured recently. The same five-page marketing site, deployed three ways. On a shared origin server with no CDN, median time to first byte was 480 milliseconds. Behind a CDN but still rendered on a Node server per request, first byte dropped to 220 milliseconds. As static files on CDN edge nodes, first byte was 35 milliseconds. Same content, same design. The only variable was how the bytes reached the browser. Static-on-edge won by more than ten times, and that saving flows straight into a faster LCP.
Third-party scripts: the silent tax
Third-party scripts — analytics, chat widgets, ad tags, social embeds, A/B testing — are the most overlooked performance cost on business sites. Each one is a request, a download, and execution time on your main thread. They add up fast.
We have audited sites where 70% of the total page weight was third-party scripts the owner barely remembered installing. A chat widget here, a heatmap tool there, three different analytics tags, a Facebook pixel, a TikTok pixel. The owner had no idea their homepage was executing twenty tracking scripts on every load.
Audit ruthlessly. List every third-party script on your site. For each one, ask: do we actually use this, and is the value worth the performance cost? You will usually find scripts you can remove entirely.
Delay non-critical scripts. Analytics and marketing pixels do not need to load before the page is usable. Load them after the page becomes interactive, or on a delay. The data is essentially identical and the user experience is dramatically better.
Tag managers. If you must run many marketing tags, a tag manager like Google Tag Manager at least gives you one controlled place to manage them and a way to fire them on triggers. But a tag manager is not a performance fix — it is a management tool. You still pay for every tag it fires.
Use server-side where possible. Server-side analytics and conversion APIs move the work off the user’s browser and onto your server. The data is often more accurate and the user pays no performance cost.
Measuring performance
You cannot improve what you do not measure. Here is the toolkit, free to expensive.
Lighthouse. Built into Chrome DevTools. Runs a lab audit of any page and gives you scores for Performance, Accessibility, Best Practices, and SEO, plus a detailed list of opportunities with estimated savings. The starting point for every performance conversation.
PageSpeed Insights. Google’s hosted version of Lighthouse, plus real-user field data from the Chrome User Experience Report. Always look at both the lab score and the field data — field data is what your actual visitors experience.
Chrome DevTools Performance tab. Records a timeline of everything the browser does while loading and interacting with your page. Shows you exactly which scripts run, how long they take, and what blocks the main thread. The tool for diagnosing specific problems.
WebPageTest. The most detailed testing tool available. Lets you test from specific locations, devices, and connection speeds, and produces a filmstrip of how the page painted over time. Indispensable for hard problems.
Real User Monitoring (RUM). Lab tools test in simulated conditions. RUM collects performance data from your actual visitors, on their actual devices, on their actual networks. This is the truth. Cloudflare Web Analytics, SpeedCurve, and similar services provide it.
The discipline is: measure in the lab to find problems, then confirm in the field that your fixes worked for real users.

Performance budgets
A performance budget is a rule you set and enforce: this page will not ship more than X kilobytes of JavaScript, will not make more than Y requests, and will not score below Z on Lighthouse. Without a budget, performance regresses. Every new feature adds a little weight, and over a year the site is slow again and nobody remembers how it happened.
Set the budget based on your goals. If you want a sub-one-second mobile load, your JavaScript budget is probably under 50KB. Be honest about what that rules out.
Enforce it automatically. Integrate a check into your build pipeline that fails the build when the budget is exceeded. Lighthouse CI and bundlesize do this. A budget that nobody enforces is a wish, not a budget.
Review the budget regularly. When someone proposes adding a feature that breaks the budget, the conversation is not “how do we sneak this in” but “what do we remove to make room.” This is how fast sites stay fast over years.
Why Astro wins on performance by default
Most of this guide has been about fighting against frameworks that ship too much. Astro flips the default. Instead of starting heavy and optimizing down, Astro starts at zero and adds only what you ask for.
Static HTML by default. Astro renders every page to static HTML at build time. The browser receives HTML and CSS. No framework runtime. No hydration step. No virtual DOM. The page paints the moment the bytes arrive.
Islands architecture. When a component genuinely needs interactivity — a contact form, a pricing toggle, an interactive demo — you mark it as an island. Astro ships a small, self-contained chunk of JavaScript for just that component and hydrates only it. Everything around it stays static. The total JavaScript for a page is measured in single-digit kilobytes, not hundreds.
Zero JavaScript by default. This is the line worth repeating. An Astro page with no interactive components ships zero kilobytes of JavaScript. Not a little. Zero. The fastest framework runtime is the one you never ship.
Built-in image optimization. Astro’s image component generates optimized formats, responsive sizes, and correct dimensions automatically. You write one image tag; Astro produces AVIF, WebP, fallbacks, and the right srcset.
Edge-ready output. Astro deploys to static CDN hosts, serverless platforms, and edge runtimes. The default static output is a folder of files that can live on any CDN edge worldwide.
The result, measured: every site we build scores in the green on all three Core Web Vitals and hits 100 on Lighthouse Performance out of the box. Not because we are performance wizards. Because the stack makes the fast path the default path, and we do not have to spend our budget fighting the framework.
Common performance mistakes
We have audited a lot of sites. The same mistakes show up almost everywhere. Avoid these and you are ahead of most of the web.
Testing only on desktop. Covered above, but it deserves repeating. If you never test on a throttled mid-range phone, you are optimizing for the wrong device.
Unoptimized hero images. The single most common LCP killer. A 2MB JPEG at the top of the page, served at full resolution to a phone that displays it at 400 pixels wide. Compress, resize, serve modern formats, and preload.
Render-blocking JavaScript in the head. Synchronous scripts that the browser must download and execute before it can paint. Defer almost everything.
Shipping a full framework for a static page. A five-page brochure site loading 200KB of React to display text. The framework is doing nothing the visitor can see and costing a second of load time.
Ignoring third-party scripts. The silent tax. A chat widget, three analytics tags, two marketing pixels, all loading on every page. Audit them, remove the ones you forgot about, and delay the rest.
No caching strategy. Returning visitors download everything again because the server sends no cache headers. A few lines of configuration make the site load instantly for repeat visits.
Fonts that block text. Loading a web font without font-display: swap and without preloading. The browser waits for the font, or swaps it in late and causes layout shift.
Lazy-loading the wrong things. Lazy-loading the hero image above the fold actively hurts LCP. Lazy-loading is for below-the-fold content only.
No performance budget. Without one, every new feature adds weight and nobody notices until the site is slow again. Set a budget and enforce it in the build.
The pattern across all of these: they are not exotic bugs. They are defaults left unchanged. Performance is mostly the discipline of not shipping what you do not need.
The order of operations, if you do nothing else
Read this whole guide and want the short version? Here is the priority order.
- Measure. Run your homepage through PageSpeed Insights. Look at the mobile score and the field data. That tells you how bad it is and where.
- Fix images. Compress them, serve modern formats, set dimensions, lazy-load below the fold. This alone fixes LCP on most sites.
- Cut JavaScript. Defer everything. Remove what you do not need. Replace heavy components with lighter alternatives or static HTML.
- Audit third-party scripts. Remove the ones you forgot about. Delay the ones you need.
- Move to a CDN. Serve static files from the edge. Fix your time to first byte.
- Set a budget and enforce it. Stop the regression from happening again.
Do those six things and your site will be faster than 90% of the web. Do them on Astro and most of them are done for you before you start.
The one idea to take away
Performance is not a feature you add. It is a default you choose at the start, and a discipline you maintain forever afterward. The teams that win are the ones that decided fast was a requirement before they wrote the first line of code, not the ones trying to optimize their way out of a slow stack three years in.
Build light. Measure honestly. Cut relentlessly. Your visitors, your rankings, and your revenue will all reward you for it.
Want a site that is fast by default instead of fast by accident? We build hand-coded sites that score in the green out of the box. See our pricing or tell us about your project.
Need a website like this?
We build fast, SEO-optimized sites that actually rank. From $199.
Get in touch →