DEV Community

Cover image for A Technical SEO Audit Should End in a Decision, Not a Score
Bertrand Morel for Edikka

Posted on Originally published at edikka.com

A Technical SEO Audit Should End in a Decision, Not a Score

A technical SEO audit can report 97% health and still miss the one defect that should stop a release.

An accidental noindex on a key template, an empty application shell, a canonical pointing at a redirect, or a production firewall blocking required resources does not become less serious because 43 other checks are green.

That is the problem with audit scores: they compress different risks, evidence levels, and unknowns into one reassuring number.

I would rather have an audit that answers six harder questions:

  1. What exactly did we test?
  2. What evidence did we observe?
  3. What remains unknown?
  4. Which failure blocks the release?
  5. Who owns the correction and the decision?
  6. When will we replay the check?

This article presents the decision model behind an open technical SEO protocol we use at Edikka. It contains 44 replayable checks across 13 domains, but the number is not the point. The point is to make every conclusion falsifiable and every release decision explicit.

It does not predict rankings, traffic, rich results, or AI citations.

Replace the score with a decision rule

Every applicable check needs two separate classifications:

  • Status: Not tested, Compliant, Non-compliant, or Not applicable.
  • Severity: Blocking, Major, Minor, or Information.

Then apply a rule that people can challenge:

IF an applicable Blocking check is Non-compliant
   OR an applicable Blocking check is Not tested
THEN NO-GO

ELSE IF a Major non-compliance remains
THEN ARBITRATION REQUIRED

ELSE GO WITH RESERVATIONS
Enter fullscreen mode Exit fullscreen mode

This is an Edikka governance rule, not an industry standard. A team may choose stricter thresholds. What matters is that the rule is written before the result is known.

The uncomfortable part is intentional: Not tested is not the same as Compliant.

Four evidence levels prevent false certainty

Technical audits often merge facts observed from outside a site with private states that only the site owner or search engine can reveal.

Keep them separate.

Evidence level Examples What it can establish What it cannot establish
Public HTTP response, robots.txt, source HTML, rendered DOM, sitemap, JSON-LD What a documented client observed at a documented time What Google crawled, selected, or indexed
Search Console URL Inspection, Google-selected canonical, Pages and Core Web Vitals reports The state reported for the property and inspected sample The single cause of a ranking change
Server logs Verified crawler requests, response codes, frequency, bytes An interaction received by the infrastructure How the fetched content was subsequently used
Configuration CDN, WAF, CMS, deployment and routing rules The configured intent, once tested That every edge, cache key, and route behaves identically

A public audit can establish that a page declares a coherent canonical. It cannot honestly claim that Google selected that canonical without Search Console evidence.

That boundary improves the audit. It turns “Google has indexed the right page” into two testable statements:

  • Public: the page, sitemap, redirects, and internal links converge on the intended URL.
  • Private: URL Inspection reports the intended Google-selected canonical.

Locate the broken stage before proposing a fix

“SEO-friendly” is too vague to debug. A URL passes through distinct stages:

Stage Diagnostic question Useful evidence
Discovery Does a public path lead to the URL? HTML links, sitemap, referring URLs in logs
Crawling May the crawler request it? robots.txt, HTTP response, verified logs
Rendering Does critical content exist after execution? Source HTML, rendered DOM, URL Inspection
Indexing Which URL and content did the engine retain? URL Inspection and indexing reports
Serving Is the page selected for this query and context? Search performance and observed results

A 200 response proves that the server returned a successful representation. It does not prove discovery, indexation, or selection for a query.

A sitemap declares candidate URLs. It is not an indexation certificate.

Start with a real GET request

Do not rely on a HEAD request alone. Applications, CDNs, and firewalls can handle HEAD and GET differently.

Record the final status, complete redirect count, effective URL, and duration:

TARGET_URL="https://example.com/important-page"

curl --location --silent --show-error --output /dev/null \
  --write-out 'status=%{http_code}\nredirects=%{num_redirects}\nfinal=%{url_effective}\ntime=%{time_total}s\n' \
  "$TARGET_URL"
Enter fullscreen mode Exit fullscreen mode

Replay more than the happy path:

  • the canonical URL;
  • an HTTP or host variant;
  • an old redirected URL;
  • a removed resource;
  • a URL that never existed.

The expected result is not “everything returns 200.” The expected result is that each case returns the status and destination intended for that resource.

Test intent, mechanism, and evidence together

Many technical SEO failures begin with the right intent and the wrong mechanism.

Intent Primary mechanism Evidence to retain Dangerous shortcut
Reduce crawling robots.txt for compliant crawlers Parsed rule plus logs Treating it as access control
Remove a page from an index Accessible noindex directive Source/header plus URL Inspection after recrawl Blocking the page before the crawler can read noindex
Consolidate duplicates Redirect or rel="canonical", depending on the case Converging signals plus selected canonical Canonicalising genuinely different pages
Protect private data Server-side authentication and authorisation Anonymous request denied with no sensitive body Publishing the data and hiding it from robots

Google's robots directives documentation explicitly notes that crawlers must be allowed to access a page to read its noindex rule. The Robots Exclusion Protocol also defines crawling rules, not a security boundary.

Compare raw HTML and rendered output

Avoid arguments such as “React is bad for SEO” or “SSR solves SEO.” Framework labels are not evidence.

For every priority template, compare at least:

  • the raw HTML response;
  • the rendered DOM in a browser;
  • URL Inspection for the private Google view.

This small Playwright probe records a few invariants from the rendered page:

import { chromium } from "playwright";

const url = process.argv[2];
if (!url) throw new Error("Usage: node inspect-page.mjs <url>");

const browser = await chromium.launch();
const page = await browser.newPage();
const response = await page.goto(url, { waitUntil: "networkidle" });

const evidence = await page.evaluate(() => ({
  title: document.title,
  h1: [...document.querySelectorAll("h1")].map((node) =>
    node.textContent?.trim(),
  ),
  canonical:
    document.querySelector('link[rel="canonical"]')?.getAttribute("href") ?? null,
  robots:
    document.querySelector('meta[name="robots"]')?.getAttribute("content") ?? null,
  crawlableLinks: document.querySelectorAll("a[href]").length,
  jsonLdBlocks: document.querySelectorAll(
    'script[type="application/ld+json"]',
  ).length,
  mainTextCharacters:
    document.querySelector("main")?.textContent?.trim().length ?? 0,
}));

console.log(JSON.stringify({
  requestedUrl: url,
  finalUrl: page.url(),
  status: response?.status() ?? null,
  ...evidence,
}, null, 2));

await browser.close();
Enter fullscreen mode Exit fullscreen mode

This does not reproduce Googlebot or prove indexation. It provides replayable browser evidence that can be compared with the raw response and Search Console.

Seven useful release gates

The complete protocol is larger, but these seven checks expose why severity matters more than a percentage:

Gate Evidence expected Typical severity
Final public URL returns the intended response after a controlled redirect chain URL, timestamp, final status, effective URL Blocking
Required resources are crawlable and no accidental noindex exists robots.txt, headers, source HTML Blocking
Critical content exists in source HTML or observable rendered output Raw response, rendered DOM, inspection Blocking
Canonical signals converge Final URL, canonical, sitemap, internal links Major
Important pages receive crawlable HTML links Source URL, destination, anchor, status Major
JSON-LD describes visible entities without invented claims Parsed graph plus visible-content comparison Major
Field Core Web Vitals are read separately from laboratory scores LCP, INP, CLS at the 75th percentile by device/group Major

Notice the last row: a Lighthouse score is diagnostic laboratory evidence. It is not field evidence, and a score of 100 does not guarantee good real-user Core Web Vitals.

Store an audit result as data

If a conclusion cannot be exported, compared, and replayed, it will be difficult to govern after the next deployment.

A minimal result might look like this:

{
  "id": "TS25",
  "url": "https://example.com/product/42",
  "observed_at": "2026-08-25T13:00:00Z",
  "status": "Non-compliant",
  "severity": "Blocking",
  "access": "Public",
  "evidence": {
    "raw_html": "main content absent",
    "rendered_dom": "main content present after API response"
  },
  "limitation": "Local Chromium is not Google URL Inspection",
  "owner": "Frontend platform",
  "decision": "NO-GO"
}
Enter fullscreen mode Exit fullscreen mode

The limitation field is as important as the observation. It prevents a local browser result from silently becoming a claim about Google.

Put stable invariants in CI

Not every audit check belongs in CI. The stable ones often do:

  • final status and redirect budget;
  • accidental robots or noindex changes;
  • canonical presence and consistency;
  • hreflang reciprocity;
  • critical server-delivered content;
  • essential crawlable links;
  • JSON-LD syntax and required visible evidence.

Use representative fixture URLs for each important template. Preserve the timestamped report as a build artifact. Link it to the deployment. Replay it after release.

Automation detects a defined regression. It does not own the business exception or the final release decision. Every exception still needs an owner, a reason, and an expiry date.

The open protocol

The full method currently contains 44 checks across 13 domains: HTTP, robots, indexation, canonicalisation, sitemaps, internal discoverability, URL spaces, internationalisation, JavaScript, semantic HTML, structured data, performance, edge behaviour, CI, and crawler policies.

The assets are open under CC BY 4.0:

Primary references used by the protocol include RFC 9110 for HTTP semantics, RFC 9309 for the Robots Exclusion Protocol, Google's canonicalisation guidance, JavaScript SEO documentation, and the Core Web Vitals threshold methodology.

The question I would ask before your next deployment is not:

What technical SEO score did we get?

It is:

Which applicable failure would stop this release, and do we have the evidence to detect it?

What is the technical SEO invariant that should be a NO-GO in your stack?


AI-assistance disclosure: this DEV edition was adapted from my original Edikka protocol with AI assistance for structure and English editing. The audit model, controls, evidence boundaries, examples, verification, and publication decision remain my responsibility.

Top comments (0)