Gerris home
Gerris

Chris Abraham: technical consulting, AI tools, and technical SEO

Home › Guides › SEO Build Checks

How to add SEO regression tests to your website's build

Most SEO damage on a coded website arrives in an ordinary pull request. Someone refactors a layout, and the canonical tag disappears from 4,000 pages. A content import runs, and every description is blank. Nobody notices for a month, because nobody was looking. A short set of automated checks, run on every build, looks every time. Here is what to test and how to wire it in.

Where the checks run

Continuous integration runs your tests automatically whenever code is pushed. On GitHub Actions, the results appear on the pull request itself, so a reviewer sees a failed check before anyone merges. SEO tests belong in the same place as the unit tests your developers already run: they're just tests whose subject is the built HTML.

The simplest pattern is to build the site, then run a script over the output. A static or prerendered site produces a folder of HTML files to read directly. A server rendered site can be started in the CI job and fetched page by page from a list of representative URLs, one per template.

The checks worth having

Titles and descriptions

Indexing signals

Sitemap agreement

Structured data

Headings and markup

Links

A minimal example

A check doesn't need a framework. This Python sketch reads every built page and fails the build on a missing or duplicate title:

import pathlib, re, sys
seen, problems = {}, []
for page in pathlib.Path("out").rglob("*.html"):
    html = page.read_text(encoding="utf-8")
    titles = re.findall(r"<title>(.*?)</title>", html, re.S)
    if len(titles) != 1:
        problems.append(f"{page}: {len(titles)} title elements")
        continue
    seen.setdefault(titles[0].strip(), []).append(str(page))
for title, pages in seen.items():
    if len(pages) > 1:
        problems.append(f"duplicate title {title!r}: {pages}")
print("\n".join(problems))
sys.exit(1 if problems else 0)

Add a step to the CI workflow that runs it after the build. A nonzero exit fails the check, and the message names the page and the rule, so the developer can fix it without asking anyone. For anything beyond a sketch, use a real HTML parser in place of regular expressions.

Checks that guard the writing, too

Build checks can protect content as well as markup. This site's build compares the text of every page with every other page and fails when two pages share more than 15% of their six word phrases, or when a sentence of ten or more words appears on two pages. That keeps near duplicate pages from creeping in as the site grows. A similar rule suits any site that publishes many pages from a shared template, such as location or category pages.

Rolling it out without blocking everyone

  1. Start in report only mode. Run the checks and print the results without failing the build, and fix the existing backlog.
  2. Turn on failure one rule at a time, starting with the ones that cost the most when they break: canonicals, noindex, and robots.txt.
  3. Keep messages specific. "Missing canonical on /products/blue-widget/" gets fixed in a minute. "SEO check failed" gets the check switched off.
  4. Make the rules a file in the repository, reviewed like code, so changing a threshold is a decision someone signs off.

What tests can't tell you

Build checks prove the HTML you ship meets your rules. They can't tell you how Google ranks it, whether a title persuades anyone to click, or what an AI assistant says about you. Pair them with a crawl of the live site after each release and with Search Console, and keep a person reading the pages.

References