A Lighthouse 100 is an honest claim about how a page loads and no claim at all about whether it works. That sounds obvious, until a perfect score ends up as a merge gate or a launch checklist item. We ran it 30 times this morning. A planted checkout that charges the card twice on every load scored 100 for performance ten runs in a row. Our own homepage moved 25 performance points between two runs with no code change. Use the score as a load budget, read it as a median, and never let it stand in for a test.
Why now
"Removing Next.js got my blog to 100 on every Lighthouse category" is on the Hacker News front page today, and the thread under it has turned into a roll call of perfect scores. benmccann: "I got straight 100s using SvelteKit" (comment). SwellJoe, on a static blog: "I just checked and it's 100 on pagespeed. The magic of a static HTML blog is you don't have to think about performance" (comment).
Both are fair. A static page that scores 100 really is fast. The question for anyone shipping product, not a blog, is what else the 100 is taken to mean.
What we ran
Lighthouse 13.5.0 from npm, headless Chrome on macOS, default settings: mobile form factor, simulated throttling. Ten runs each, back to back, on three targets:
- Our homepage, a Next.js page, live.
- One of our blog posts, live.
- A local checkout page we wrote for this. It is plain HTML, one serif font, a labelled card field and a Pay button. Its Pay handler swallows the submit and does nothing. Its script sends
POST /api/chargetwice on page load. Our local server answers 200 and counts each charge.
| Target | Performance, 10 runs | Accessibility | Best practices | SEO |
|---|---|---|---|---|
| Homepage | 65, then 89 to 90 (median 89) | 96 every run | 77 every run | 100 every run |
| Blog post | 81 to 97 (median 91) | 95 every run | 77 every run | 100 every run |
| Planted checkout | 100 every run | 100 every run | 96 every run | 100 every run |
The server logged 20 charges across the 10 checkout runs: two per page load, each answered 200.
What it got right
The three non-performance categories are deterministic. Accessibility, best practices and SEO gave the same number on all ten runs of every page. They are rule checks on the rendered page, and they behave like rule checks.
It found a real cost on our own site. Best practices sat at 77 on both live pages. The reason was not our code: our session-analytics script sets third-party cookies, and Lighthouse lists every one, by name and domain. That is a trade we made on purpose, and the score is right to charge us for it.
The performance metrics are useful as numbers. Largest contentful paint on the homepage settled at about 3.5 seconds after the first run. That is a clear, actionable figure.
Where the score moves
The homepage's first run scored 65, with an LCP of 8.6 seconds. The next nine scored 89 or 90, with LCP between 3.5 and 3.7 seconds. The blog post's first run was also its worst, at 81, and its best was 97. The first run of each live page being the slowest suggests a cold cache somewhere between us and the server. We did not prove that.
Lighthouse's own documentation expects this. Its variability guide says performance scores "will change due to inherent variability in web and network technologies, even if there hasn't been a code change", and tells you to run it multiple times "before drawing conclusions" (docs). A gate on one run of the performance score is a flaky test by design: on our homepage, a threshold of 80 would have failed one run in ten with nothing changed.
What it cannot see
The checkout is the sharp result. It scored 100 for performance, accessibility and SEO on every run, and lost four best-practice points to a missing favicon. The page cannot take a payment, and it charges the card twice before anyone touches it.
The evidence was in the report. The network-requests table lists both POST /api/charge calls, each with status 200. No audit reads that table for meaning, because no audit knows that one page load should make zero charges. That is not a Lighthouse bug. It loads the page and measures it; it never clicks Pay, and it has no idea what Pay is for.
We saw the same shape this morning in impeccable's detect CLI: a static check gives a clean bill to a page that does the wrong thing. Lighthouse adds a twist. It recorded the wrong thing, and then scored it perfect.
Verdict
Keep Lighthouse. Gate on accessibility and best practices, which are stable and catch real problems. Treat performance as a budget: run it five or more times and compare medians, or you will be retrying a flaky gate. And keep a separate check for what the page is for, one that clicks Pay and counts the charges. A perfect score told us the broken checkout was fast, accessible and well indexed. All three were true.
Sources
- 01korindiehacker: Removing Next.js got my blog to 100 on every Lighthouse categorykorindiehacker.bloffy.blog
- 02Hacker News: discussion threadnews.ycombinator.com
- 03Hacker News: SwellJoe, on a static blognews.ycombinator.com
- 04Hacker News: benmccann, straight 100snews.ycombinator.com
- 05GitHub: GoogleChrome/lighthouse, Score Variabilitygithub.com
- 06GitHub: Lighthouse v13.5.0 releasegithub.com
// The agent clicked "Pay". Did the right things actually happen?
Example from the README
Your agent says it works. Reticle checks the network, state and console inside your running app, and names the line to fix.
85 of 88 seeded bugs caught in the public benchmark