Web Development Guide
Website Performance Optimization
A technical guide to building fast websites that convert. From Core Web Vitals to server-side rendering, learn the strategies that reduce load times, improve user experience, and boost search rankings.
Prerequisites
- Access to Google Search Console and PageSpeed Insights for your domain
- Ability to modify server configuration and HTTP headers
- A build pipeline that supports asset hashing and code splitting
- Basic understanding of HTML, CSS, and JavaScript performance concepts
- Hosting environment that supports CDN integration
Measuring Performance with Core Web Vitals
You can't optimize what you don't measure. Google's Core Web Vitals provide three standardized metrics that quantify user experience: Largest Contentful Paint (LCP), Interaction to Next Paint (INP), and Cumulative Layout Shift (CLS). As of 2025, these metrics directly influence search rankings, and Google reports that pages meeting all three thresholds see 24% fewer page abandonments.
LCP measures how quickly the main content of a page loads. A good LCP is under 2.5 seconds. INP replaced First Input Delay in March 2024 and measures overall responsiveness throughout a page visit, not just the first interaction. A good INP score is under 200 milliseconds. CLS measures visual stability, with a good score below 0.1. Each metric captures a different dimension of user experience.
Use multiple measurement tools to get a complete picture. Google PageSpeed Insights provides both lab and field data. Chrome DevTools' Lighthouse panel offers detailed diagnostics and suggestions. The Chrome User Experience Report (CrUX) provides real-world performance data from actual Chrome users. For continuous monitoring, tools like SpeedCurve or Calibre can track performance over time and alert you to regressions.
Establish performance budgets for each metric. A performance budget sets hard limits, for example, LCP must remain under 2.0 seconds, total page weight under 1.5MB, and no more than 30 HTTP requests per page. Integrate these budgets into your CI/CD pipeline so performance regressions are caught before deployment.
Largest Contentful Paint (LCP)
Target under 2.5 seconds. Measures when the largest visible element finishes rendering.
Interaction to Next Paint (INP)
Target under 200ms. Measures responsiveness to user interactions throughout the page visit.
Cumulative Layout Shift (CLS)
Target under 0.1. Measures visual stability and unexpected layout shifts during page load.
Performance Budgets
Set hard limits on page weight, request count, and load times. Enforce in CI/CD pipelines.
Image Optimization
Images typically account for 40-60% of a page's total weight, making them the single biggest performance lever for most websites. The HTTP Archive reports that the median web page includes 900KB of images. Effective image optimization can reduce this by 50-80% without visible quality loss.
Start with modern formats. WebP offers 25-35% better compression than JPEG at equivalent quality and supports transparency. AVIF, the newer format based on AV1 video codec, provides 20% better compression than WebP but has slower encoding times. Use the HTML picture element with source sets to serve the best format each browser supports, with JPEG or PNG as the fallback.
Implement responsive images using the srcset attribute to serve appropriately sized images for each device. A 2000px wide hero image displayed on a 375px mobile screen wastes 80% of the downloaded bytes. Generate multiple sizes at build time (320w, 640w, 960w, 1280w, 1920w) and let the browser choose the right one.
Lazy load images below the fold using the native loading="lazy" attribute or Intersection Observer API for more control. Never lazy load the LCP image, as this directly hurts your largest contentful paint score. For above-the-fold images, use fetchpriority="high" to signal the browser to prioritize the download. Consider using blur-up placeholder techniques or solid color placeholders to prevent layout shifts while images load.
Modern Formats
Serve WebP or AVIF with JPEG/PNG fallbacks using the picture element for broad compatibility.
Responsive Images
Use srcset to deliver appropriately sized images for each viewport width.
Lazy Loading
Defer off-screen images with loading='lazy' but never lazy load the LCP image.
Compression
Target 80-85% JPEG quality. Use tools like Sharp, Squoosh, or ImageOptim for batch processing.
Code Splitting & Lazy Loading
Modern JavaScript applications often ship megabytes of code that users don't need immediately. Code splitting breaks your application into smaller chunks that load on demand, reducing initial bundle size and improving time to interactive. According to Web Almanac data, the median page ships 509KB of JavaScript, but only 36% of that code is actually executed during the initial page load.
Route-based code splitting is the most impactful starting point. Each page or route should load only the JavaScript it needs. Frameworks like Next.js, Nuxt, and Astro handle this automatically. For single-page applications using React or Vue, dynamic imports with React.lazy() or defineAsyncComponent() split components into separate chunks loaded when the route is accessed.
Component-level code splitting targets heavy UI elements like modals, charts, maps, and rich text editors. These components should load when the user triggers them, not on initial page load. A charting library like Chart.js or D3 can add 200KB+ to your bundle. Loading it only when the user navigates to a dashboard page saves that weight for everyone else.
Tree shaking removes unused code from your bundles during the build process. Modern bundlers like Vite, Webpack 5, and Rollup perform tree shaking automatically when you use ES module imports. Avoid importing entire libraries when you only need specific functions. Import { debounce } from 'lodash-es' instead of import _ from 'lodash' to avoid bundling the entire 72KB library. Regularly audit your bundle with tools like webpack-bundle-analyzer or source-map-explorer to identify bloat.
Route-Based Splitting
Load only the JavaScript needed for each page. Frameworks like Next.js handle this automatically.
Component-Level Splitting
Defer heavy components like charts, maps, and modals until the user interacts with them.
Tree Shaking
Use ES module imports and modern bundlers to automatically eliminate unused code from bundles.
Bundle Analysis
Audit bundle sizes regularly with webpack-bundle-analyzer or source-map-explorer.
Caching Strategies
Caching is the most effective performance optimization available because the fastest request is one that never reaches the server. A well-implemented caching strategy can reduce server load by 80-95% and cut page load times by 50-70% for returning visitors.
Browser caching uses HTTP Cache-Control headers to tell browsers how long to store assets locally. Static assets like images, fonts, CSS, and JavaScript should use immutable, long-lived cache headers (Cache-Control: max-age=31536000, immutable) with content-hashed filenames. When the file changes, the hash changes, and the browser fetches the new version. HTML pages should use shorter cache times or no-cache with ETag validation to ensure users see fresh content.
Application-level caching stores computed results so they don't need to be recalculated on every request. This includes database query results, API responses, rendered HTML fragments, and session data. Redis and Memcached are the most popular in-memory caching solutions, offering sub-millisecond response times for cached data. Use cache invalidation strategies (TTL, event-based, or write-through) appropriate to your data's update frequency.
Service workers provide a programmable cache layer in the browser, enabling offline functionality and instant repeat page loads. The Cache API stores full HTTP responses, and you control the caching strategy per route. Use a cache-first strategy for static assets and a network-first strategy for dynamic content. Workbox simplifies service worker implementation with preconfigured strategies and automatic cache management.
Browser Caching
Use immutable, long-lived Cache-Control headers for hashed static assets and no-cache with ETags for HTML.
Application Caching
Cache database queries, API responses, and computed results with Redis or Memcached.
Service Workers
Implement programmable caching with cache-first for static assets and network-first for dynamic content.
Cache Invalidation
Choose TTL, event-based, or write-through invalidation strategies based on data update frequency.
CDN Implementation
A Content Delivery Network distributes your website's assets across globally distributed edge servers, reducing latency by serving content from the location nearest to each user. Without a CDN, a user in Tokyo requesting content from a server in Virginia experiences roughly 200ms of network latency per round trip. A CDN can reduce this to under 30ms.
Modern CDNs go far beyond simple file distribution. Platforms like Cloudflare, Fastly, and AWS CloudFront offer edge computing capabilities, DDoS protection, automatic image optimization, and intelligent routing. Cloudflare Workers and Fastly Compute allow you to run custom logic at the edge, enabling personalization and A/B testing without round trips to your origin server.
Configure your CDN to cache different content types appropriately. Static assets (images, CSS, JS, fonts) should be cached at the edge with long TTLs, typically 30 days or more. HTML pages can be cached with shorter TTLs or use stale-while-revalidate to serve cached content while fetching fresh versions in the background. API responses should be cached selectively based on endpoint volatility.
Implement cache purging for content that needs immediate updates. Most CDNs offer both individual URL purging and tag-based purging, where you tag content and purge by tag when underlying data changes. Monitor your CDN's cache hit ratio, which should exceed 90% for static assets. A low hit ratio indicates misconfigured cache headers or insufficient TTLs. Use the CDN's analytics to identify your slowest routes and highest-traffic pages, then optimize caching for these critical paths first.
Choose the Right CDN
Evaluate Cloudflare, Fastly, and CloudFront based on your edge computing, security, and geographic needs.
Configure Cache TTLs
Set long TTLs for static assets and shorter TTLs with stale-while-revalidate for dynamic content.
Cache Purging
Implement URL-level and tag-based cache purging for content that requires immediate updates.
Monitor Hit Ratios
Target 90%+ cache hit ratios for static assets. Investigate and fix low hit ratio routes.
Server-Side Rendering & Static Generation
Rendering strategy directly impacts performance, SEO, and user experience. Server-side rendering (SSR) generates HTML on the server for each request, delivering fully rendered pages to the browser. Static site generation (SSG) pre-renders pages at build time. Each approach has distinct performance characteristics, and modern frameworks like Next.js, Nuxt, and Astro let you mix strategies on a per-page basis.
SSG provides the best performance for content that doesn't change with each request. Pages are pre-rendered to HTML at build time and served directly from a CDN. Time to first byte (TTFB) is typically under 50ms because no server computation is required. SSG is ideal for marketing pages, blog posts, documentation, and product catalog pages. The tradeoff is that content updates require a rebuild and deployment.
SSR generates HTML on each request, making it suitable for personalized content, authenticated pages, and data that changes frequently. SSR typically adds 100-500ms to TTFB compared to SSG, depending on server location and computation complexity. Optimize SSR performance with response caching, streaming SSR (which sends HTML in chunks as it's generated), and edge-side rendering.
Incremental Static Regeneration (ISR) bridges the gap between SSG and SSR. Pages are statically generated at build time, then regenerated on demand when the cached version exceeds a specified revalidation interval. This gives you the performance of static pages with the freshness of server rendering. Astro's hybrid rendering and Next.js ISR both implement this pattern effectively. Choose your rendering strategy based on content freshness requirements, personalization needs, and your traffic volume.
Static Site Generation
Pre-render pages at build time for sub-50ms TTFB. Ideal for content that changes infrequently.
Server-Side Rendering
Generate HTML per request for personalized or real-time content. Optimize with caching and streaming.
Incremental Static Regeneration
Combine static performance with dynamic freshness by revalidating pages on configurable intervals.
Hybrid Rendering
Mix SSG, SSR, and ISR on a per-page basis using frameworks like Astro or Next.js.
Related Content
Web Accessibility Compliance Guide
Practical web accessibility guide covering WCAG 2.2 standards, semantic HTML, ARIA, keyboard navigation, color contrast, and accessibility testing tools.
API Integration Guide
Complete API integration guide covering REST and GraphQL fundamentals, authentication methods, error handling patterns, rate limiting, and monitoring strategies.
How to Choose the Right Tech Stack
A decision framework for choosing frontend frameworks, backend languages, databases, and hosting. Make technology decisions that scale with your business.
CMS Selection Guide for Business
Compare headless vs traditional CMS platforms, evaluate requirements, and choose the right content management system for your business website or application.