HomeProjectsBlogContact

Why Is My Website Slow? A Step-by-Step Diagnosis

Don't guess at performance. Measure first, then work through server response, images, JavaScript, fonts and third-party scripts in order. A diagnostic guide built around Core Web Vitals.

Why Is My Website Slow? A Step-by-Step Diagnosis

"The site feels slow" is a symptom, not a diagnosis. The cause could be a slow database query, a 4MB hero image, half a megabyte of JavaScript or a chat widget loading before your content. Random optimisation wastes time. A methodical diagnosis finds the real bottleneck quickly.

Step 1: measure before changing anything

Start with PageSpeed Insights on your real URL. It shows two kinds of data:

  • Field data: how real visitors experienced your site over recent weeks (from the Chrome UX Report, when there's enough traffic). This is what Google uses for search signals.
  • Lab data: a simulated load on a mid-range phone with throttled network. Great for debugging, but it's one synthetic run.

Then open Chrome DevTools: the Network tab (with throttling enabled) and the Performance panel show exactly what loads and what blocks.

Run lab tests a few times. Scores vary between runs, so look at trends, not a single number.

Step 2: understand the three Core Web Vitals

Metric Measures Good target
LCP (Largest Contentful Paint) When the main content appears 2.5 s or less
INP (Interaction to Next Paint) How quickly the page responds to taps, clicks and keys 200 ms or less
CLS (Cumulative Layout Shift) How much content jumps around while loading 0.1 or less

Each points at different causes, so figure out which one is failing before you start fixing.

Step 3: check server response time (TTFB)

Time to First Byte is how long the browser waits before receiving anything at all. If TTFB is slow, nothing else can be fast.

Common causes:

  • Slow database queries: missing indexes or the N+1 problem.
  • No caching: rendering the same page from scratch on every request. (Caching strategies.)
  • Cold starts on serverless functions or sleeping free-tier servers.
  • Distance: a server on another continent, with no CDN in front.
  • Waterfalls on the server: several API calls in sequence that could run in parallel.

Fix: static generation or caching for public pages, a CDN, indexed queries, and parallel data fetching.

Step 4: fix the LCP element

PageSpeed Insights tells you which element is the LCP. Usually it's a hero image or a large heading.

If it's an image:

  • Serve modern formats (WebP or AVIF) at the right size. Don't ship a 3000px image into a 600px slot.
  • Use responsive images (srcset and sizes), or your framework's image component.
  • Don't lazy-load the LCP image. Lazy loading is great below the fold, harmful above it. Add fetchpriority="high" to the hero image.
  • Preload it if the browser discovers it late (for example, when it's a CSS background).

If it's text:

  • Make sure web fonts don't block rendering. Use font-display: swap or optional, preload the main font file, and limit the number of font weights.
  • Check that the text isn't hidden behind a JavaScript animation that starts at opacity: 0. The browser may not count it as painted until the animation runs.

Step 5: reduce JavaScript

JavaScript is the most expensive resource byte for byte. It has to be downloaded, parsed, compiled and executed, often on a slow phone.

  • Analyse your bundle with your bundler's analyser. Look for huge dependencies you barely use (entire icon sets, a date library for one format, a chart library on a page without charts).
  • Split by route so each page loads only what it needs.
  • Load heavy components on demand: modals, editors, maps, charts.
  • Render on the server where possible, so the browser receives HTML instead of building it.

Large JavaScript also hurts INP. Long tasks block the main thread, so taps feel laggy. Break up long work, avoid huge re-renders, and defer non-urgent work.

Step 6: stop layout shifts

CLS comes from content moving after it first appears:

  • Images and videos without dimensions. Always set width and height (or aspect-ratio) so space is reserved.
  • Ads, embeds and banners inserted above existing content. Reserve space for them.
  • Web fonts swapping in with different metrics. Use fallback font metric overrides or a well-matched fallback.
  • Content injected at the top of the page after load, such as cookie banners. Overlay them instead of pushing content down.

Step 7: audit third-party scripts

Analytics, tag managers, chat widgets, A/B testing tools, social embeds. Each one adds requests and main-thread work, and you don't control how heavy they are.

  • Remove scripts nobody uses anymore. Every site has a few.
  • Load the rest with defer or async, or after the page becomes interactive.
  • Load chat widgets on interaction, for example when someone clicks a "Chat" button.
  • Use lightweight alternatives where possible, such as a privacy-friendly analytics script instead of a heavy suite.

Step 8: confirm with real users

Lab tests on your laptop aren't your users' experience. After shipping fixes:

  • Watch field data in PageSpeed Insights and Search Console's Core Web Vitals report over the following weeks.
  • Consider collecting real-user metrics with the web-vitals library, sent to your analytics.
  • Test on an actual mid-range phone over a mobile connection at least once.

The priority order

When everything looks slow, work in this order:

  1. Server response time (TTFB)
  2. The LCP element (usually an image or font)
  3. JavaScript size and long tasks
  4. Layout shifts
  5. Third-party scripts

Fix the biggest bottleneck first, re-measure, repeat.

FAQ

Why is my website slow on mobile but fast on desktop?

Phones have slower CPUs and often slower networks. Large JavaScript bundles and unoptimised images hurt them most. Test with CPU and network throttling to see what mobile users experience.

Do Core Web Vitals affect SEO?

They're part of Google's page experience signals. Content relevance matters more, but between similar pages, a faster experience helps, and it improves conversions regardless.

What is a good page load time?

Aim for LCP under 2.5 seconds, INP under 200 milliseconds and CLS under 0.1 for most real visitors, measured at the 75th percentile of field data.

Keep reading

More from the workbench

All articles →