Catching SEO Regressions with Lighthouse CI

Manual audits catch problems after they ship; the goal of a mature workflow is to catch them before merge. Lighthouse CI runs Lighthouse against a preview build on every pull request and fails the check when the SEO or performance score drops below a budget — turning indexability into a gate rather than a periodic review. It is the automation layer of the SPA audit workflow and the cheapest possible place to catch a rendering regression.

Lighthouse CI as a required regression gate on every pull request Each pull request builds a preview, Lighthouse runs against it in headless Chromium, and a score-budget assertion either lets the merge through or blocks it. Every pull request runs the gate before merge Pull request a code change Preview build per-PR deploy Lighthouse CI headless Chromium SEO score ≥ budget? pass fail Merge OK Blocked
A required check: the score budget on the preview build decides whether the change can merge or is blocked.

Step-by-step fix

  1. Add Lighthouse CI to the pipeline. Run it against a preview URL for each pull request so every change is measured before it merges.

    # .github/workflows/lhci.yml
    - run: npm run build && npm run preview &
    - run: npx @lhci/cli autorun --collect.url=http://localhost:4173/products/widget-x
  2. Assert on SEO and performance budgets. Configure assertions so a regression fails the build instead of silently lowering the score.

    // lighthouserc.js
    module.exports = {
      ci: {
        assert: {
          assertions: {
            'categories:seo':         ['error', { minScore: 0.95 }],
            'categories:performance':  ['warn',  { minScore: 0.80 }],
            'document-title':          'error',  // fails if <title> missing
            'meta-description':        'error',  // fails if description missing
          },
        },
      },
    };
  3. Make the check required. In branch protection, mark the Lighthouse CI status as required so a regression cannot be merged.

    ❌ Lighthouse runs but is advisory → regressions merge anyway
    ✅ Lighthouse status is required   → SEO score drop blocks the merge
  4. Test the templates that matter. Point the collector at one URL per template (product, article, listing), not just the homepage, so template-level regressions are caught.

Each assertion carries a severity that decides what a failure does: an error breaks the build and blocks the merge, while a warn is logged for the trend without stopping the pipeline. Reserve error for the audits that are non-negotiable for indexability.

Lighthouse CI assertion severities and what each failure does A matrix mapping each configured assertion to its severity and the effect of a failure, showing which audits break the build and which only warn. Assertion Severity If it fails categories:seo error build fails below 0.95 categories:performance warn logged, does not block document-title error build fails if the title is missing meta-description error build fails if the description is gone crawlable-anchors error build fails on non-crawlable links
Severity is the lever: the indexability audits are set to error so a regression blocks the merge, while performance only warns.

Validation

  • A pull request that removes the <title> fails the document-title assertion.
  • The SEO category stays at or above your minScore on every merge.
  • The check is required and visibly blocks merges when it fails.
  • Trend reports (LHCI server or uploaded artifacts) show scores stable or improving over time.

Plotting the SEO score per pull request over time makes the gate visible: most changes stay above the budget line and merge, and the one that dips below it is the regression the check caught and blocked.

SEO score per pull request against the budget line A line chart of the Lighthouse SEO score across successive pull requests, with a budget line at 0.95; one pull request drops below the line and is blocked while the rest pass. SEO score per pull request 1.00 0.80 SEO budget 0.95 blocked successive pull requests →
The budget line turns the SEO score into a gate: the one pull request below 0.95 is failed and blocked before it can merge.

Reference

// Minimal lighthouserc.js for SEO gating across templates
module.exports = {
  ci: {
    collect: {
      url: [
        'http://localhost:4173/',
        'http://localhost:4173/products/widget-x',
        'http://localhost:4173/blog/example-post',
      ],
    },
    assert: {
      assertions: {
        'categories:seo': ['error', { minScore: 0.95 }],
        'document-title': 'error',
        'meta-description': 'error',
        'crawlable-anchors': 'error',  // SEO: links must be crawlable <a href>
      },
    },
  },
};
The reference lighthouserc.js in two halves: what to collect and what to assert The config splits into a collect block listing one URL per template and an assert block whose four rules each fail the build, gating every collected template on the same SEO checks. The config in two halves: collect, then assert lighthouserc.js collect · one URL per template / /products/widget-x /blog/example-post assert · fail the build categories:seo ≥ 0.95 — error document-title — error meta-description — error crawlable-anchors — error
The minimal config is just a list of template URLs to render and a set of non-negotiable assertions that each block the merge on failure.

Frequently Asked Questions

Can Lighthouse catch rendering problems in a CI pipeline? Yes. Lighthouse renders the page in headless Chromium and reports the SEO category, including whether the document has a title, meta description, and crawlable content. Asserting on those audits in CI fails the build when a change strips metadata or breaks rendering.

Should Lighthouse CI run on the production URL or a preview? Run it against a per-pull-request preview deployment so regressions are caught before merge. Running only against production catches problems after they have already shipped and possibly been crawled.

← Back to SEO Audit Workflows for Client-Side Apps