July 25, 20266 min read

Our SEO Audit Said Our JavaScript Was Broken. It Wasn't. The Real Problem Was Worse.

Free spatial notes

Put this workflow on an infinite canvas instead of another linear doc.

Start in the browser with no account. Create an account only when you want to sync what you made.

The Alert

An automated site audit emailed us: "Page has broken JavaScript — 2 URLs."

That is the kind of alert that produces an immediate urge to go change something. Broken JavaScript on a site that is entirely a JavaScript application sounds serious, and there is a strong pull toward finding a plausible culprit and shipping a fix.

We resisted that, and it was the right call.

Reproducing It First

Before changing anything, we tried to reproduce the finding. That meant crawling the site the way the auditor does — all 194 live HTML pages, using the auditor's own user agent, checking every referenced script.

Results:

  • Zero page failures
  • Zero JavaScript failures
  • Every page referenced the same current hashed bundle
  • That bundle returned 200 with the correct content type

The finding was not reproducible on production.

What Had Actually Happened

The timing lined up with a deployment.

Our site is a static build with content-hashed asset filenames. Every deploy produces a new bundle hash and new HTML referencing it. During the turnover window, a crawler can fetch an HTML page from the old deployment and then request its script after the new deployment has replaced it. The page is real. The script reference is real. It just no longer exists by the time the crawler asks for it.

Result: a broken-JavaScript report for a site that was never broken for any human visitor, whose page load and script fetch happen within milliseconds of each other rather than across a crawl queue.

The exact two URLs were only visible inside the authenticated audit report, which meant we could not check them directly — another reason to verify by independent crawl rather than trust the summary.

The Decision Not to Fix It

We made no code changes. This deserves saying explicitly, because "the audit said X, so we changed Y" is an extremely common and mostly harmful pattern.

Changing code to satisfy an unreproducible alert has a poor risk profile. You have not fixed a known problem, because there is no known problem. You have added a change, and changes carry risk. Meanwhile the alert clears on the next crawl regardless, and you now believe your change did it — which means the next time it appears you will make another unnecessary change.

The correct response to an unreproducible finding is to document why it is not reproducible and rerun the crawl. If it recurs on a stable deployment, it is real and worth investigating properly.

There is a legitimate underlying question here about whether stale asset references during deployment turnover matter at all. For human visitors, essentially never — the window is seconds and the failure requires fetching HTML and JS on opposite sides of it. For crawlers with queued requests, occasionally. It is a known characteristic of hash-based cache-busting deployments, not a defect.

The Thing We Actually Found

The crawl we ran to disprove the alert surfaced a genuine problem the alert had not mentioned.

Every non-root sitemap URL was redirecting. Our sitemap generator appended a trailing slash to each path. Our server serves those paths without one — a request for /pricing returns 200, and a request for /pricing/ returns a 307 to /pricing.

So every entry in the sitemap pointed at a redirect, and the sitemap disagreed with the canonical tag the pages themselves emitted.

This is a substantially bigger deal than the reported issue:

  • Every sitemap entry costs a redirect hop before reaching real content
  • The sitemap and the canonical tags were telling crawlers two different things
  • Crawl budget goes to redirects instead of pages
  • It affects the entire sitemap, not two pages

And unlike the JavaScript alert, it was completely reproducible and unambiguously wrong.

A Footnote On Getting This Backwards

Our first write-up of this finding said the opposite: that the sitemap and canonicals were missing a trailing slash the site required. That was wrong, and it survived for weeks because it was never checked against the server.

The correction took two commands — request a URL with the slash, request it without, compare status codes. That is all it takes to determine which form a site actually canonicalizes, and it is worth doing before writing a line of code, because a fix built on the reversed diagnosis would have added trailing slashes everywhere and pointed all 217 canonical tags at redirects instead.

Which is a more expensive version of the same mistake this post is about: acting on a plausible description of a problem rather than a verified one.

Why the Audit Missed the Bigger Problem

Automated audits are pattern matchers. They are good at finding the patterns they know and they report those patterns with equal confidence regardless of severity. A transient script fetch failure and a systematic canonical URL mismatch both arrive as findings in a list.

The severity ranking has to come from you, because the tool does not know that one of those affects two pages transiently and the other affects your entire sitemap permanently.

What we would now do with any audit report:

  1. Reproduce independently before changing anything. Use the tool's user agent, crawl it yourself.
  2. Check the deployment timeline. Transient findings clustering around a deploy are usually about the deploy.
  3. Count the blast radius. Two URLs versus 192 URLs should determine the order of work, not which alert had a scarier name.
  4. Pay attention to what you find while investigating. The reproduction crawl is often more informative than the original report.
  5. Do not ship a speculative fix. An unreproducible finding plus a code change equals an unverifiable outcome.

The Broader Point

Automated tooling is genuinely useful and we keep running it. But an alert is a hypothesis, not a diagnosis. The gap between them is a reproduction attempt, and that attempt is where the real findings tend to show up.

We spent an afternoon proving that a reported problem was not a problem. That afternoon also produced the single highest-value SEO fix on our list. Neither outcome would have happened if we had simply patched something and marked the ticket done.

See the site the crawler was arguing about — or just try the product.

30 days free. No credit card required.

Try the Brainstorm Web template in OmniCanvas

Open a local canvas with the Brainstorm Web layout ready to go — no setup or account needed.

Use the Brainstorm Web Template

Start locally, then sign up only to sync — or explore the interactive demo first.