Web Performance Basics: From First Byte to Interactive
Why Performance Matters
Every 100 ms of delay costs you conversions. Google reports that 53% of mobile users abandon sites that take longer than 3 seconds to load. Performance is not a luxury — it is the foundation of user experience and search ranking.
This guide walks through the metrics, the rendering pipeline, and the strategies you need to ship fast pages.
Core Performance Metrics
Modern performance measurement focuses on what users actually perceive. These are the metrics that matter:
| Metric | Full Name | Measures | Good | Poor |
|---|---|---|---|---|
| FP | First Paint | First pixel on screen | ≤ 1 s | > 3 s |
| FCP | First Contentful Paint | First text or image renders | ≤ 1.8 s | > 3 s |
| LCP | Largest Contentful Paint | Main content visible | ≤ 2.5 s | > 4 s |
| TTI | Time to Interactive | Page responds to input | ≤ 3.8 s | > 7.3 s |
| CLS | Cumulative Layout Shift | Visual stability | ≤ 0.1 | > 0.25 |
| INP | Interaction to Next Paint | Input responsiveness | ≤ 200 ms | > 500 ms |
| TTFB | Time to First Byte | Server response speed | ≤ 800 ms | > 1.8 s |
Google's Core Web Vitals (LCP, CLS, INP) directly influence search rankings. If you track nothing else, track these three.
How the Metrics Relate
Metrics fire in sequence:
- TTFB — Server responds
- FP — Browser paints the first pixel (often a blank screen)
- FCP — First useful content appears (text, image, canvas)
- LCP — Largest above-the-fold element finishes rendering
- TTI — JavaScript event handlers attached, page is usable
Each metric depends on the previous one. A slow TTFB cascades into slow everything else.
The Critical Rendering Path
The critical rendering path is the sequence of steps the browser takes to turn HTML, CSS, and JavaScript into pixels on screen.
Step-by-Step
- Parse HTML → Build the DOM tree
- Fetch and parse CSS → Build the CSSOM tree
- Combine DOM + CSSOM → Render tree (visible nodes only)
- Layout → Calculate geometry (size, position)
- Paint → Fill pixels (colors, images, borders)
- Composite → Layer compositing for final screen output
JavaScript can interrupt this path. When the parser hits a <script> tag, it stops building the DOM until the script executes (unless the script is async or defer).
Optimizing the Path
| Technique | What It Does | Impact |
|---|---|---|
| Inline critical CSS | Renders above-the-fold content without waiting for external CSS | Faster FCP |
defer on <script> | Parses HTML first, executes scripts after DOM ready | Faster FCP/TTI |
async on <script> | Downloads in parallel, executes whenever ready | Good for analytics |
| Preload key resources | <link rel="preload"> fetches early | Faster LCP |
| Avoid render-blocking CSS | Split CSS into critical and deferred chunks | Faster FCP |
<!-- Critical CSS inlined in <head> -->
<style>body{margin:0;font-family:sans-serif}.hero{font-size:2rem}</style>
<!-- Non-critical CSS loaded asynchronously -->
<link rel="preload" href="styles.css" as="style"
onload="this.rel='stylesheet'">
<!-- Scripts deferred to avoid blocking parsing -->
<script src="app.js" defer></script>
Resource Prioritization
Not all resources are equally important. The browser assigns priorities based on type and position, but you can override them.
Priority Hints
Use fetchpriority to tell the browser what matters most:
<!-- High priority: hero image above the fold -->
<img src="hero.jpg" fetchpriority="high" alt="Hero image">
<!-- Low priority: below-the-fold content -->
<img src="supplementary.jpg" fetchpriority="low" alt="Supplementary image">
<!-- High priority: critical script -->
<script src="core.js" fetchpriority="high"></script>
Resource Hints
| Hint | Purpose | When to Use |
|---|---|---|
dns-prefetch | Resolve DNS early | Third-party domains |
preconnect | Establish connection (DNS + TCP + TLS) | Critical third-party origins |
preload | Fetch resource early with high priority | Hero images, critical fonts, key CSS |
prefetch | Fetch for future navigation | Next-page resources |
modulepreload | Preload ES modules | Critical JS modules |
<!-- Preconnect to your API and CDN -->
<link rel="preconnect" href="https://api.example.com">
<link rel="preconnect" href="https://cdn.example.com">
<!-- Preload the LCP image -->
<link rel="preload" as="image" href="hero.webp">
Caching Strategies
Caching prevents repeat downloads. Combine multiple layers for maximum effect:
Browser Cache (HTTP Headers)
| Header | Purpose | Typical Value |
|---|---|---|
Cache-Control | Directives for caching behavior | max-age=31536000, immutable |
ETag | Validation token for revalidation | Auto-generated by server |
Last-Modified | Timestamp for conditional requests | File modification date |
For static assets with hashed filenames, use aggressive caching:
Cache-Control: public, max-age=31536000, immutable
The immutable flag tells the browser to skip revalidation entirely — the filename changes when the content changes, so the cached version is always valid.
Service Worker Cache
Service workers give you programmatic control over caching:
// Cache-first strategy for static assets
self.addEventListener('fetch', (event) => {
if (event.request.url.includes('/assets/')) {
event.respondWith(
caches.match(event.request).then((cached) => {
return cached || fetch(event.request)
})
)
}
})
CDN Cache
CDNs serve your content from edge servers close to users. Benefits include:
- Reduced TTFB (response comes from nearby edge, not origin)
- Automatic compression (gzip/Brotli)
- DDoS absorption
- SSL termination offloaded from origin
Configure CDN cache based on content type: long TTL for static assets, short TTL or stale-while-revalidate for HTML.
Performance Budgets
A performance budget sets numeric limits on metrics and resource sizes. It prevents gradual bloat from eroding speed over time.
Setting a Budget
| Resource | Suggested Budget | Rationale |
|---|---|---|
| Total page weight | < 1 MB (compressed) | Fits in slow 3G within 5 s |
| JavaScript | < 200 KB (compressed) | Keeps TTI under 5 s on mobile |
| CSS | < 50 KB (compressed) | Fast CSSOM construction |
| Images | < 500 KB total | Largest content type, easiest to optimize |
| LCP | < 2.5 s | Core Web Vitals "good" threshold |
| CLS | < 0.1 | No jarring layout shifts |
| TTI | < 3.8 s | Users can interact quickly |
Enforcing Budgets
Integrate budget checks into your CI pipeline:
// performance-budget.js (Lighthouse CI config)
module.exports = {
ci: {
assert: {
assertions: {
'categories:performance': ['error', { minScore: 0.9 }],
'first-contentful-paint': ['error', { maxNumericValue: 1800 }],
'largest-contentful-paint': ['error', { maxNumericValue: 2500 }],
'cumulative-layout-shift': ['error', { maxNumericValue: 0.1 }],
'total-byte-weight': ['warn', { maxNumericValue: 1000000 }],
},
},
},
}
Builds fail when budgets are exceeded, forcing the team to address performance regressions immediately.
Quick Wins Checklist
- Compress all text resources with Brotli or gzip
- Serve images in WebP or AVIF with responsive
srcset - Defer non-critical JavaScript
- Inline critical CSS, lazy-load the rest
- Set
Cache-Control: immutableon hashed assets - Preload the LCP image and critical fonts
- Add
widthandheightto images to prevent CLS - Configure a performance budget in CI
Key Takeaways
- Track LCP, CLS, and INP — Google's Core Web Vitals that affect rankings
- The critical rendering path determines how fast pixels appear; minimize render-blocking resources
- Use
fetchpriority,preload, andpreconnectto prioritize what matters - Layer browser cache, service workers, and CDN for resilient caching
- Performance budgets prevent gradual bloat — enforce them in CI
- Every 100 ms counts: faster pages retain more users and rank higher
Related Guides
- HTML Minification: Reducing Page Size for Faster Loading
- HTML Whitespace and Comment Optimization Techniques
- CSS Minification Guide
Try It Yourself
Minify your HTML and see the size difference with our free HTML Minifier tool. Paste your markup, configure optimization options, and get leaner output in seconds.