A complete guide to improving your website's loading speed, passing Google's Core Web Vitals, and boosting your SEO rankings. Covers images, JavaScript, caching, CDN, server optimization, and monitoring — with specific fixes you can implement today.
Check your website speed score free — results in ~30 seconds:
Page speed isn't just a technical metric — it directly affects your revenue, user retention, and search rankings.
Speed optimization has a compounding effect: faster sites rank higher, attract more organic traffic, convert better, and earn more revenue — which funds further optimization. Slow sites create the opposite spiral.
For small businesses and SMBs without dedicated engineering teams, the good news is that most speed improvements follow a clear priority order: fix images first (biggest gains, lowest effort), then JavaScript, then server/caching. You don't need to do everything at once.
Google's Core Web Vitals are three specific metrics that measure real-world user experience. Passing all three is required to get the Page Experience ranking boost.
Measures how long it takes for the largest image or text block to load. Usually your hero image, banner, or above-the-fold heading.
Fix: Optimize your hero image, preload critical resources, improve server response time.
Measures how responsive your page is to user interactions (clicks, keyboard input, taps). Replaced FID in March 2024.
Fix: Reduce JavaScript execution time, break up long tasks, defer non-critical scripts.
Measures visual stability — how much elements shift around as the page loads. Unexpected layout shifts frustrate users and cause mis-clicks.
Fix: Always specify width/height on images and embeds, avoid inserting content above existing content, use CSS transforms for animations.
How to measure Core Web Vitals: Google PageSpeed Insights shows lab data. Google Search Console → Core Web Vitals shows real-user (field) data. PageGuard provides instant performance scores you can check on demand.
Images are typically the #1 cause of slow websites — often accounting for 50–80% of page weight. This is where you'll get the biggest speed gains.
WebP images are 25–35% smaller than JPEG/PNG at equivalent quality. AVIF is even smaller (another 20%) but has slightly lower browser support. Use <picture> with AVIF + WebP fallbacks for maximum compatibility. Most CMS platforms (WordPress, Shopify) have plugins that auto-convert on upload.
Tools: Squoosh.app (free, browser-based), ImageOptim (Mac), Sharp (Node.js library for automated pipelines).
Don't serve a 2000px-wide image when the container is 400px wide. Use the srcset attribute to serve different sizes for different screens. For hero images: generate 400w, 800w, 1200w, 1600w versions. Responsive images can reduce image weight by 70%+ on mobile devices.
Example: srcset="hero-400.webp 400w, hero-800.webp 800w, hero-1200.webp 1200w" sizes="(max-width: 768px) 100vw, 50vw"
Always specify width and height attributes on <img> tags. Without these, the browser can't reserve space before the image loads — causing layout shift (CLS) when the image finally arrives. This single fix often resolves poor CLS scores.
Add loading="lazy" to all images that are not visible on initial page load. This defers loading until the user scrolls near the image, reducing initial page weight significantly. Exception: never lazy-load your hero image or any image visible without scrolling (use loading="eager" for those).
The LCP image (usually your hero image) needs to start loading as early as possible. Add a <link rel="preload"> tag in the <head>: <link rel="preload" as="image" href="hero.webp">. This can improve LCP by 200–500ms.
Render-blocking CSS and JavaScript are the second most common cause of slow sites. These resources prevent the browser from rendering any visible content until they fully load and execute.
Minification removes whitespace, comments, and unnecessary characters from code files without changing functionality. Typically reduces file size by 10–30%. Most build tools (Webpack, Vite, Parcel) minify automatically in production mode. For WordPress, plugins like Autoptimize or WP Rocket handle this.
Add defer or async attributes to scripts that aren't needed for initial render. defer loads the script in parallel but executes it after HTML parsing completes. async loads and executes as soon as possible. Use defer for most scripts; async for independent scripts like analytics.
Most sites load entire CSS frameworks (Bootstrap, Tailwind) but only use 5–20% of the styles. Tools like PurgeCSS or Tailwind's built-in purge scan your HTML and remove unused styles. This can reduce CSS from 200KB to under 10KB — a massive improvement. Modern build tools with Tailwind v3/v4 do this automatically.
For JavaScript-heavy applications (React, Vue, Angular), use code splitting to load only the JavaScript needed for the current page. Dynamic imports (import()) let you defer loading components until they're needed. Next.js, Nuxt, and SvelteKit do this automatically per-route.
Identify the CSS needed to render above-the-fold content ("critical CSS") and inline it directly in the <head>. Then load the rest of your stylesheet asynchronously. This eliminates the render-blocking CSS bottleneck for initial viewport rendering. Tools: Critical (npm), Penthouse, or manually identify and inline ~5–8KB of critical styles.
Your server's Time to First Byte (TTFB) is the foundation of all other speed metrics. If your server is slow, no amount of frontend optimization will make your site fast.
TTFB measures how long before the browser receives the first byte of your HTML. Target under 800ms. Causes of high TTFB: slow database queries, no server-side caching, cold-starting serverless functions, resource-constrained shared hosting, or distant server location. Use Cloudflare (free tier) as a reverse proxy to cache HTML at the edge and reduce TTFB dramatically.
HTTP/2 allows multiple requests over a single connection (multiplexing), eliminating the head-of-line blocking problem in HTTP/1.1. HTTP/3 goes further by using QUIC protocol for faster connection establishment and better performance on lossy networks. Most modern hosting providers and CDNs enable HTTP/2 by default — check your server headers to verify.
For WordPress or other CMS platforms with database-driven content, server-side page caching generates static HTML snapshots of dynamic pages. Instead of rebuilding the page on every request, the server returns the cached HTML instantly. Plugins: WP Super Cache (free), WP Rocket (paid). For custom apps, use Redis or Memcached for object caching.
Physical distance between your server and user adds latency (roughly 1ms per 100km for round-trip). If most of your users are in Europe but your server is in the US, you're adding 80–150ms of unnecessary latency. Use a CDN to serve static assets globally, or choose a hosting provider with data centers near your primary audience.
Caching and compression are quick wins — often implementable with a few lines of server configuration — that can dramatically reduce load times for returning visitors.
Tell browsers to cache static assets (images, CSS, JavaScript) locally for extended periods. Returning visitors load these assets from their browser cache instead of your server. Recommended: Cache-Control: public, max-age=31536000, immutable for versioned assets (1 year). For HTML: Cache-Control: no-cache (always validate freshness).
Text-based assets (HTML, CSS, JavaScript) compress dramatically — typically 70–80% size reduction. Brotli achieves 15–25% better compression than GZIP. Enable at the server level (nginx: brotli on;) or via your CDN. Verify compression is working by checking response headers for Content-Encoding: br or gzip.
For long-cached assets, add a content hash to filenames: style.a4f8d2.css. When you update the file, the hash changes, forcing browsers to fetch the new version — while unchanged files remain cached. Modern bundlers (Webpack, Vite) do this automatically. Avoid using query strings for cache busting (style.css?v=2) — some CDNs and proxies ignore query strings.
A Content Delivery Network (CDN) serves your assets from servers geographically close to your users, reducing latency for visitors worldwide.
CDNs cache static files (images, CSS, JavaScript) at edge nodes worldwide. A user in Tokyo gets your assets from a Tokyo data center, not your server in Virginia. Cloudflare (free tier) is the easiest starting point — just point your DNS to it. For more control, AWS CloudFront, Fastly, or BunnyCDN offer excellent performance and competitive pricing.
Add <link rel="preconnect"> hints for third-party origins your page uses early (fonts.googleapis.com, CDN domains, analytics). This establishes the DNS + TCP + TLS connection early so it's ready when resources are requested. Add crossorigin attribute for CORS requests.
Example: <link rel="preconnect" href="https://fonts.googleapis.com">
Use <link rel="prefetch"> to pre-load pages users are likely to navigate to next (product detail pages, next step in a checkout flow). The browser fetches these resources in idle time, making subsequent navigation near-instant. Don't prefetch everything — only high-probability next pages.
Third-party scripts (analytics, chat widgets, ad trackers, social embeds) are often the biggest performance killers. A single poorly-optimized third-party script can add 300–500ms to your page load.
Use Chrome DevTools → Network tab to see all third-party requests. For each script ask: Is it essential? Can it be replaced with a privacy-friendly alternative? Consider switching from Google Analytics to Plausible or Fathom (smaller scripts, no consent banners needed in many jurisdictions).
Never load analytics, chat, or marketing scripts without async or defer. These scripts should never block your main content from rendering. Most third-party vendors provide async code snippets — use those, not the synchronous fallbacks.
Chat widgets (Intercom, Drift, Zendesk) can add 200–500KB of JavaScript. Load them only after the page is interactive, on user interaction, or after a delay. Use IntersectionObserver or a 3-second setTimeout to defer chat initialization. For video embeds (YouTube, Vimeo), use a facade (placeholder image) that loads the actual embed only when clicked — this can save 500KB+ on pages with embedded videos.
Web fonts can cause Flash of Invisible Text (FOIT) or Flash of Unstyled Text (FOUT), contributing to LCP delay and CLS. Proper font loading strategy prevents these issues.
Add font-display: swap to your @font-face declarations. This tells the browser to show text in a system font immediately, then swap to your custom font when it loads — eliminating invisible text while fonts download. For Google Fonts, add &display=swap to the URL.
Fonts used in above-the-fold headings are critical — preload them: <link rel="preload" as="font" href="/fonts/heading.woff2" type="font/woff2" crossorigin>. Only preload fonts used immediately — preloading too many fonts competes with other critical resources.
Serving fonts from your own domain (or CDN) eliminates the extra DNS lookup and connection to fonts.googleapis.com. Use google-webfonts-helper.herokuapp.com to download self-hostable versions of Google Fonts. Host the WOFF2 files on your server or CDN with proper cache headers.
Full font files include all characters (Cyrillic, Greek, arrows, currency symbols, etc.) even if you only use Latin characters. Subsetting removes unused characters, reducing font file size by 50–80%. Use unicode-range in CSS to load only the character subsets you need, or subset the font file itself using fonttools.
Speed optimization is not a one-time project. CMS updates, new plugins, new pages, and new third-party scripts constantly introduce regressions. You need ongoing monitoring to catch them early.
Use PageGuard to automatically scan your website daily or weekly. When your performance score drops below a threshold, you get an email alert — before your users notice the slowdown. Monitoring catches regressions from new plugins, CMS updates, or new page templates that accidentally load heavy scripts.
Google Search Console → Core Web Vitals shows real-user (field) data from Chrome users visiting your site. This is more representative than lab tests because it reflects actual network conditions, device capabilities, and usage patterns. Check this monthly and compare against your performance budget targets.
Before deploying major changes (new theme, redesign, new landing page), run a performance test and compare against your baseline. Set a performance budget: if the score drops more than 5 points or LCP exceeds 3 seconds, investigate before deploying to production. PageGuard makes this easy with on-demand scanning.
PageGuard checks your Performance, SEO, Accessibility, and Best Practices scores and sends you email alerts when something drops. No tech skills required.
Complete technical SEO guide covering sitemaps, HTTPS, structured data, and more.
Deep dive into LCP, INP, CLS — what they measure and how to improve them.
Check your website's performance score and Core Web Vitals in ~30 seconds.
How PageGuard compares to Google's PageSpeed Insights tool.