Blog/Web Development
Web Development10 min read

Why Your Website Is Slow and How to Fix It: A Developer's Checklist

Slow websites lose money. Google penalises them in search, and users leave before the page finishes loading. This checklist walks through every layer of the stack where speed problems hide.

Ali Dawood
Ali Dawood

CTO at ASPIRED Digital

A slow website is not just annoying. It directly costs you leads, sales, and search rankings. Google has made this explicit: Core Web Vitals are a ranking factor. Users have made it even more explicit by leaving. The average user expects a page to load in under two seconds. Miss that window and your bounce rate climbs fast.

This is a developer-focused checklist. Not vague advice about "making things faster", but specific, ordered actions you can take to diagnose and fix speed problems on any website. If you want professional help implementing these changes, our web development team does this daily.

Start With Measurement

Before you change anything, measure. You need baseline numbers or you will not know if your changes helped.

  • Google PageSpeed Insights gives you lab data (Lighthouse) and field data (Chrome User Experience Report). Field data matters more because it reflects what real users experience.
  • WebPageTest.org lets you test from different locations and connection speeds. Use it to simulate how your site loads on a 3G connection in a different country.
  • Chrome DevTools Performance tab records a waterfall of everything that happens during page load. This is where you find the bottlenecks.

Record your Largest Contentful Paint (LCP), Cumulative Layout Shift (CLS), and Interaction to Next Paint (INP) scores. These three metrics are your Core Web Vitals.

Largest Contentful Paint: Get the Main Content Visible Fast

LCP measures how long it takes for the largest visible element to render. That is usually a hero image, a heading, or a video poster. Google wants this under 2.5 seconds.

Fix Your Images First

Images are the number one cause of slow LCP. Here is what to do:

  • Use AVIF with WebP fallback. AVIF files are typically 50% smaller than JPEG at the same visual quality. WebP sits between AVIF and JPEG. Serve AVIF to browsers that support it, WebP to the rest. The HTML <picture> element handles this natively.
  • Set explicit width and height attributes on every <img> tag. The browser uses these to reserve space before the image loads, which also prevents CLS.
  • Preload your hero image. Add <link rel="preload" as="image" href="/hero.avif" type="image/avif"> in the <head>. This tells the browser to start fetching the image before it encounters it in the DOM.
  • Lazy load everything below the fold. Use loading="lazy" on images that are not immediately visible. Never lazy load the hero image or anything above the fold.

Reduce Server Response Time

Your server's Time to First Byte (TTFB) directly delays everything else. If the server takes 800ms to respond, your LCP cannot possibly be under 2.5 seconds on a slow connection.

  • Use a CDN. Cloudflare, Fastly, or AWS CloudFront will serve cached content from edge locations close to your users. This alone can cut TTFB by hundreds of milliseconds for geographically distributed audiences.
  • Enable HTTP/2 or HTTP/3 on your server. These protocols allow multiple requests to be multiplexed over a single connection.
  • If you are running a CMS like WordPress, add server-side page caching. Without it, every request hits the database.

Eliminate Render-Blocking Resources

CSS and synchronous JavaScript in the <head> block rendering. The browser will not paint anything until these files are downloaded and parsed.

  • Inline critical CSS directly in the HTML. Tools like critical (npm package) can extract the CSS needed for above-the-fold content. Load the rest asynchronously.
  • Defer non-essential JavaScript. Add defer or async to script tags that do not affect initial render. Analytics scripts, chat widgets, and tracking pixels should all be deferred.

Cumulative Layout Shift: Stop the Page From Jumping

CLS measures unexpected layout shifts. When content moves around as the page loads, users click the wrong thing. Google's threshold is 0.1 or lower.

Reserve Space for Dynamic Content

Every element that loads asynchronously (images, ads, embeds, iframes) needs reserved space. Use CSS aspect-ratio or explicit dimensions.

Font Loading Strategy

Web fonts are a common CLS offender. The browser renders text in a fallback font, then swaps to the custom font when it loads. The size difference causes a layout shift.

  • Use font-display: optional if you can tolerate the fallback font being shown permanently when the custom font is slow to load. This eliminates font-swap CLS entirely.
  • If you need the custom font, use font-display: swap combined with a size-adjusted fallback. The CSS size-adjust descriptor lets you match the fallback font's metrics to your custom font, minimising the shift.
  • Preload your font files: <link rel="preload" as="font" href="/fonts/your-font.woff2" type="font/woff2" crossorigin>
  • Self-host your fonts. Google Fonts adds a DNS lookup and connection to fonts.googleapis.com. Download the files and serve them from your own domain.

Interaction to Next Paint: Make the Page Respond Instantly

INP replaced First Input Delay in March 2024 as the responsiveness metric. It measures the delay between a user interaction (click, tap, key press) and the next visual update. Target: under 200ms.

Reduce JavaScript Bundle Size

Heavy JavaScript blocks the main thread. While the browser is parsing and executing your JavaScript, it cannot respond to user input.

  • Code split aggressively. In Next.js, dynamic imports with next/dynamic let you load components only when needed. In plain webpack or Vite projects, use import() for route-level splitting.
  • Audit your dependencies. Run npx bundlephobia your-package before adding any new library. A single charting library can add 200KB+ to your bundle.
  • Tree shake properly. Import specific functions, not entire libraries. import { debounce } from 'lodash-es' is dramatically smaller than import _ from 'lodash'.

Break Up Long Tasks

Any JavaScript task that runs for more than 50ms is a "long task" and will block user interaction. Use requestIdleCallback or scheduler.yield() (available in modern browsers) to break expensive operations into smaller chunks.

The Full Checklist

Here is the condensed version you can work through item by item:

  • Measure baseline Core Web Vitals (LCP, CLS, INP) with PageSpeed Insights
  • Convert images to AVIF/WebP and set explicit dimensions
  • Preload the hero image and lazy load everything below the fold
  • Enable a CDN and verify TTFB is under 200ms
  • Inline critical CSS and defer non-essential JavaScript
  • Self-host fonts with font-display: swap and a size-adjusted fallback
  • Code split JavaScript bundles and audit dependency sizes
  • Reserve space for all async-loaded elements to prevent layout shifts
  • Test on throttled connections and real mobile devices
  • Re-measure and compare against your baseline

Speed optimisation is not a one-time project. Every new feature, plugin, or image you add can regress performance. Build measurement into your deployment pipeline and catch regressions before they reach production.

Frequently Asked Questions

What is a good LCP score?

Google considers LCP under 2.5 seconds "good", between 2.5 and 4 seconds "needs improvement", and above 4 seconds "poor". These thresholds apply to the 75th percentile of page loads, meaning 75% of your real users need to experience the fast load time for the page to pass.

Does website speed actually affect SEO rankings?

Yes. Google confirmed Core Web Vitals as a ranking signal in 2021. The impact is real but works alongside hundreds of other signals. A fast site with thin content will not outrank a slower site with excellent content. But when content quality is comparable, speed becomes the tiebreaker. Speed also indirectly helps SEO by reducing bounce rates and increasing time on site.

Should I use a CDN even if my audience is in one country?

Yes. CDNs do more than geographic distribution. They handle DDoS mitigation, HTTP/2 and HTTP/3 support, automatic compression, and edge caching. Even for a single-country audience, a CDN typically reduces TTFB because the edge server responds faster than your origin server for cached content.

How do I check my Core Web Vitals for real users?

The Chrome User Experience Report (CrUX) collects field data from real Chrome users who have opted in. You can access it through PageSpeed Insights, the CrUX Dashboard on BigQuery, or the Search Console Core Web Vitals report. This is more reliable than lab testing because it reflects actual user conditions including device types, connection speeds, and geographic distribution.

Website SpeedCore Web VitalsLCPImage OptimisationJavaScript PerformanceCDNWeb Performance
Start a Partnership

Ready to talk?

We work with a select number of partners at a time — not everyone, the right ones. If you think there's a fit, let's find out.

AliAbdulYasserHager

Talk to a decision maker

No account managers — just the people who build