Diagnosing a slow website means following a repeatable five-step playbook: start from the single metric that's failing, run a comprehensive audit, identify the bottleneck (server, render path, asset, or main thread), confirm with real-user field data, then fix and verify one change at a time. The mistake most teams make is fixing five things in parallel and never knowing which actually helped.
Think of performance diagnosis the way a doctor approaches a patient. You don't prescribe five medications and hope one works — you take vitals, narrow the cause, intervene, then re-measure. A failing LCP and a failing INP have different root causes, and the "cure" for one can make the other worse.
Without a methodology, performance work becomes guesswork. Teams compress images that weren't the problem, ship a CDN that doesn't fix a render-blocking script, or rewrite JavaScript when the real bottleneck was a 3-second TTFB. A structured playbook prevents wasted sprints:
This is the order to investigate any performance complaint, whether it comes from a Google Search Console alert, a customer email, or your own observation that "the site feels slow."
Pick the metric that's actually failing. If Google Search Console reports poor LCP, the work is about LCP — not about the JavaScript bundle that alsolooks suspicious. Single-metric focus prevents a 4-week refactor that doesn't move the needle on the thing the user complained about.
A Greadme deep scan returns every Core Web Vital plus the specific elements (image URL, script tag, third-party origin) responsible for each one. The point is not just to see the score — it's to see which DOM nodes and resources are dragging it down.
Each Web Vital maps to a layer. Read the relationship between TTFB → FCP → LCP to localize the problem (see the diagnostic patterns table below). A bad LCP with a good FCP is a hero-image / late-loading-resource problem. A bad LCP with a bad FCP is a server problem.
Open Google Search Console → Core Web Vitals. The 28-day field data there is what Google actually uses to rank. If lab and field disagree, trust field data. Lab tools simulate one device on one network; field data is millions of real visits.
Ship one fix. Wait for the lab metric to update on a re-scan. Watch the field data over the next 28 days. Only then move to the next issue. This is slower in calendar terms but dramatically faster in "number of regressions caused."
One of the most common diagnostic puzzles. The page loads in under a second locally but Google Search Console reports a poor LCP at the 75th percentile. Here's the playbook applied:
# Step 1 — Symptom
LCP at p75 (field) = 4.6 s # Poor
LCP in dev = 0.9 s # Good
Gap = ~3.7 s --> the gap itself is the clue
# Step 2 — Comprehensive audit (Greadme deep scan)
TTFB = 1.9 s # Poor (target < 800 ms)
FCP = 2.4 s # Needs improvement (target < 1.8 s)
LCP = 4.6 s # Poor
CLS = 0.04 # Good
INP = 180 ms # Good
# Step 3 — Identify the bottleneck
TTFB is poor AND FCP is poor AND LCP is poor.
Pattern --> server tier (not hero image, not main thread).
# Step 4 — Confirm in the field
Google Search Console: LCP poor on /products/* (mobile)
LCP good on / (mobile)
--> regression isolated to product templates, mobile only
# Step 5 — Root cause + verify
Product page hits the database 14x per request (no cache).
Add edge cache for product detail (60s).
Re-scan: TTFB 1.9 s --> 220 ms, LCP 4.6 s --> 2.1 s.
Field data confirms after 28 days.The teaching point: the "dev vs prod" gap was the diagnostic clue all along. Dev hits a warm local database; production hits a cold cluster under real load. Looking only at dev numbers would have sent the team chasing the wrong layer entirely.
Memorize this table. Most performance issues fall into one of these patterns, and each pattern points to a different layer of the stack.
| Symptom Pattern | Likely Layer | First Place to Look |
|---|---|---|
| TTFB > 800 ms, FCP > 1.8 s, LCP > 4 s | Server / origin | Database queries, missing cache, cold edge regions |
| FCP < 1.8 s but LCP > 4 s | Critical render path / hero asset | Hero image size, render-blocking CSS/JS, lazy-loaded LCP element |
| CLS > 0.25 | Stability / layout | Unsized images, late-injected banners, font swap reflow |
| INP > 200 ms | Main-thread JavaScript | Long tasks, hydration cost, third-party tag managers |
| Total transferred > 3 MB | Asset weight | Unoptimized images, oversized JS bundle, unused CSS |
| Lab good, field poor | Real-world conditions | Mobile network throttling, geographic latency, third parties |
Stick to a small, reliable toolkit. Switching between half a dozen scanners produces conflicting numbers and slows diagnosis.
A page can score 95 in a lab tool and still fail Core Web Vitals at the 75th percentile in the field. Open both views before drawing conclusions. If they disagree, trust field data — that's what Google uses.
A score is a summary; the waterfall is the evidence. Open Chrome DevTools → Network and look for the longest single bar, the latest-arriving resource on the critical path, and any request that blocks render.
// In Chrome DevTools Network tab:
// 1. Disable cache
// 2. Throttle to "Slow 4G"
// 3. Reload, sort by "Waterfall"
// 4. Look for: longest bar, latest critical resource,
// any "render-blocking" badge in the Initiator column.Diagnosing on gigabit Wi-Fi hides 80% of real-world problems. Use Chrome DevTools' built-in throttling to simulate Slow 4G and a mid-tier Android CPU. Most field-data failures only reproduce under throttled conditions.
"The site is slow" is rarely true. Usually one template (product detail, search results, account settings) accounts for the bulk of the failing URLs. A crawler scan grouped by URL pattern surfaces this in minutes.
Open Chrome DevTools > Performance, record a load, and find the LCP marker on the timeline. The DevTools panel will tell you exactly which DOM node is the LCP. Half of all LCP fixes start with naming this element.
Tag managers, analytics, chat widgets, and embeds often account for 40–60% of total JavaScript on a typical site. Filter the Network tab by third-party domain and compare the bytes against first-party. If third parties dominate, that's your bottleneck — not your own code.
When TTFB and LCP both improve together, it's a server fix. When LCP improves but TTFB doesn't, it's a render-path or asset fix. The relationship between two numbers is more diagnostic than either number alone.
If the issue only shows up in field data, reproduce it locally first. Throttle the network, throttle the CPU, clear the cache. A bug you can't reproduce is a bug you can't verify you've fixed.
Once you've fixed the issue, lock in the win. Add a budget — e.g. "LCP must stay under 2.5 s on /products/*" — and fail your CI when a build regresses past it. Diagnosis isn't complete without regression prevention.
The whole point of one-change-at-a-time is that the re-scan tells you whether that specific change worked. If you ship five fixes and re-scan once, you've learned nothing about which fix mattered.
What's happening: Team compresses images for a sprint, but the actual failing metric was INP (caused by main-thread JavaScript). Image work doesn't move the needle.
Fix: Always tie the work to the failing metric. INP failures need JS audits and long-task breakup, not image compression.
What's happening: The lab score reads 96 / 100. The team declares victory. Field data still shows poor LCP at p75 because real users are on slower networks and devices than the lab simulates.
Fix: Use lab scores to guide engineering and field data (Google Search Console) to declare success. They are not interchangeable.
What's happening: Five performance changes ship together. LCP improves by 1.2 s. The team has no idea which of the five changes did the work — and which (if any) made things worse.
Fix: Ship one performance fix per release. Use feature flags if you need to keep deployment cadence high while still isolating the variable.
What's happening: Desktop Web Vitals look fine. Mobile Web Vitals are failing — and Google's ranking signal is mobile-first. Team only ever tests on desktop.
Fix: Filter Google Search Console by device type. Run all diagnostic scans on a throttled mobile profile, not desktop.
Don't check anything in the abstract — start with the specific failing metric. Open Google Search Console's Core Web Vitals report, find the URL pattern with the most failing pages, then run a Greadme deep scan on a representative URL. The combination of "which metric" and "which template" narrows the problem to one layer of the stack within minutes.
Almost always one of three causes: (1) production database is uncached or geographically distant, inflating TTFB; (2) production has third-party tags (analytics, ads, chat) that don't run in dev; (3) production traffic is mostly mobile and throttled, while dev tests run on gigabit desktop. Reproduce by throttling Chrome DevTools to Slow 4G with a mid-tier CPU profile.
Look at TTFB. If TTFB is under 800 ms but LCP is still poor, the problem is front-end (render path, hero image, blocking JS). If TTFB is over 800 ms, the problem is server-side first — fix that before touching the front end, because every front-end metric inherits the TTFB delay.
Lab data is a synthetic test on a controlled device and network — useful for engineering iteration. Field data is the Chrome User Experience Report aggregating real users over 28 days — what Google actually uses for ranking. Lab tools predict; field data confirms. When they disagree, field data wins.
Field data is a 28-day rolling average, so a single deploy won't move the dashboard immediately. Expect the Core Web Vitals report to start reflecting improvements after 7–14 days and to fully settle by day 28. In the meantime, use lab tools (Greadme deep scan, Chrome DevTools) to confirm the fix worked locally.
Indirectly but meaningfully. Generative search systems most often surface pages already ranking well in traditional search — and Core Web Vitals are part of that ranking signal. Google AI Overviews specifically prefers pages that pass Web Vitals. A slow page loses traditional ranking, which then loses AI citation share.
Set a monthly cadence at minimum, and any time you ship a major release, add a third-party tag, or change templates. Performance regressions are slow and incremental — a tag manager added in Q1 might not break INP until enough other tags accumulate in Q3.
Diagnosing performance is a discipline, not a guess. Start with the symptom, run a comprehensive audit, identify the bottleneck layer using the symptom-pattern table, confirm in the field, then fix and verify one change at a time. The teams that ship the biggest wins are the ones with the smallest changes per release.
The fastest way to apply this playbook is to skip the manual setup. Run a Greadme deep scanon the URL that's failing, follow the symptom-pattern table to localize the layer, and fix the highest-impact issue first. Then re-scan. The five-step loop is short, but it's the loop that turns a slow site into a fast one.