How to Add SEO Checks to Your CI/CD Pipeline
Step-by-step guide to adding automated SEO validation to your CI/CD pipeline. GitHub Actions, Vercel, and GitLab CI examples with exit codes and threshold configuration.
You pushed a Next.js App Router migration on Friday. Monday morning, Google Search Console shows a 40% drop in impressions. The culprit: 23 pages lost their canonical URLs, and og:image tags returned 404s due to a misconfigured asset path in your metadata export. You don't need an SEO audit tool that runs once a week and emails a PDF to the marketing team. You need a gatekeeper in your pipeline that fails the build when metadata breaks. This guide demonstrates how to wire Indxel directly into your CI/CD workflows to catch regressions before they merge.
Why run SEO checks in CI/CD?
Running SEO checks in CI/CD prevents malformed markup from reaching production by failing the build when pages violate predefined metadata rules.
CI environments require deterministic rules. A warning in a SaaS dashboard gets ignored; a failed GitHub Action forces a fix. Indxel operates exactly like ESLint or Jest. It returns standard POSIX exit codes. Exit code 0 means your pages pass the defined thresholds. Exit code 1 means a violation occurred.
When you run npx indxel check, the CLI parses your local build output or crawls your preview URL, evaluates the DOM against your configuration, and halts the pipeline if critical tags are missing. This shifts SEO validation from a post-deploy manual audit to a pre-merge automated test.
How do you configure Indxel rules for automated pipelines?
You configure Indxel rules by placing an indxel.config.json file in your project root to define strict thresholds for metadata presence, length, and validity.
A production-grade pipeline needs explicit boundaries. You define these boundaries in JSON. The configuration controls which rules trigger an exit code 1 (error), which trigger a warning (warn), and which are ignored. We recommend enforcing 15 core rules covering title length (50-60 chars), description presence, og:image HTTP status (must return 200 OK), canonical URL resolution, and JSON-LD validity.
Create indxel.config.json in your repository root:
{
"$schema": "https://indxel.com/schema.json",
"target": "http://localhost:3000",
"thresholds": {
"errors": 0,
"warnings": 10
},
"rules": {
"title-length": ["error", { "min": 30, "max": 60 }],
"description-presence": "error",
"canonical-absolute": "error",
"og-image-valid": "error",
"jsonld-schema-valid": "error",
"h1-unique": "warn"
},
"ignore": [
"/api/**",
"/admin/**"
]
}This configuration establishes a zero-tolerance policy for critical SEO failures. If a developer pushes a page with an og:image that points to a broken URL, the og-image-valid rule fails, the errors count exceeds 0, and the CLI returns exit code 1.
Set "target": ".next" to run Indxel directly against your static Next.js build output instead of spinning up a localhost server. This cuts pipeline execution time by entirely bypassing network latency.
How to add SEO validation to GitHub Actions?
Add an Indxel step to your .github/workflows/main.yml file using the npx indxel check --ci command to validate SEO during the test phase.
The --ci flag strips interactive prompts, disables terminal spinners, and formats the output for CI runners. It outputs one line per issue with the file path and rule ID, identical to ESLint's format. This allows GitHub Actions to parse the output and annotate the pull request directly.
Here is a complete workflow for a Next.js application:
name: CI
on:
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build application
run: npm run build
- name: Run Indxel SEO validation
run: npx indxel check --ci --format github
env:
INDXEL_PROJECT_ID: ${{ secrets.INDXEL_PROJECT_ID }}The --format github argument instructs the CLI to emit GitHub Actions workflow commands. When a rule fails, Indxel generates annotations directly on the changed lines of code in the PR's "Files changed" tab. The developer sees exactly which layout.tsx or page.tsx caused the canonical URL to drop.
How do you validate PRs faster using diff mode?
Use the npx indxel check --ci --diff command to analyze only the routes modified in the current pull request, reducing CI time from minutes to seconds.
Running a full crawl on a 5,000-page site takes about 4 minutes. For a pull request that modifies a single blog post, that is 3 minutes and 58 seconds of wasted compute. The --diff flag integrates with Git to identify the affected paths and scopes the validation entirely to those URLs.
When you run this command, Indxel executes the following sequence:
- Identifies the base branch (e.g.,
main). - Runs
git diff --name-onlyto find modified files. - Maps modified source files (like
app/blog/[slug]/page.tsx) to their compiled output routes. - Executes checks exclusively on the resulting routes.
A typical Next.js app with 50 pages takes 3 seconds to validate in diff mode. That saves hours of CI runner minutes per month and prevents SEO validation from becoming a bottleneck in your deployment pipeline.
$ npx indxel check --ci --diff origin/main
> Indxel: Diff mode enabled. Found 2 modified routes.
> Checking /blog/deploying-nextjs... PASS
> Checking /blog/react-server-components... PASS
> 2/2 pages pass. 0 errors, 0 warnings.How to integrate Indxel with Vercel builds?
Override your Vercel Build Command in the project settings to run npx indxel check && next build to prevent deployments with broken SEO.
Vercel's default behavior builds the application and immediately deploys it. If you want to block the deployment entirely when SEO rules fail, chain the Indxel check before the build completes. Because Vercel does not have a separate "test" phase before deployment, the build command is the correct insertion point.
Navigate to your Vercel Dashboard > Project Settings > General > Build & Development Settings. Set the Build Command to:
npm run build && npx indxel check --target .nextBy placing the check after npm run build, Indxel validates the actual production artifacts. If indxel check returns exit code 1, Vercel marks the build as failed. The bad code never reaches your preview URL, and it never merges to production.
Running Indxel inside the Vercel build step adds to your Vercel execution time. For large sites (>1,000 pages), use GitHub Actions instead to avoid hitting Vercel's maximum build duration limits.
How to configure GitLab CI for SEO pipelines?
Add an seo-validation job to your .gitlab-ci.yml file in the test stage, executing npx indxel check --ci --junit to parse results into GitLab's native test reports.
GitLab CI handles test reports via the JUnit XML format. Indxel supports this natively via the --junit flag. When you output results as JUnit, GitLab renders the SEO failures directly in the Merge Request UI under the "Tests" tab, right next to your unit tests.
stages:
- build
- test
build_app:
stage: build
script:
- npm ci
- npm run build
artifacts:
paths:
- .next/
seo_validation:
stage: test
script:
- npx indxel check --target .next --ci --junit > seo-report.xml
artifacts:
when: always
reports:
junit: seo-report.xmlThis configuration ensures the seo_validation job runs parallel to your other tests. The when: always directive guarantees the artifact uploads even if the Indxel check fails, allowing developers to download and inspect the XML report if needed.
What happens when a developer pushes broken metadata?
The CI pipeline intercepts the commit, outputs a detailed failure log with the exact file path and broken rule, and blocks the merge.
Assume a developer refactors the RootLayout component in a Next.js application. They accidentally delete the metadataBase export. This causes all relative Open Graph images to output as local paths instead of absolute URLs—a critical error for social sharing.
They push the commit and open a PR. The GitHub Action runs npx indxel check --ci. The headless browser renders the updated pages, evaluates the DOM, and immediately catches the malformed og:image tags.
The CLI outputs the following:
$ npx indxel check --ci
Target: .next (Static Build)
Pages found: 47
✖ / (app/page.tsx)
error og-image-absolute og:image must be an absolute URL. Found: "/og.png"
✖ /about (app/about/page.tsx)
error og-image-absolute og:image must be an absolute URL. Found: "/og-about.png"
✖ 2 errors, 0 warnings
✖ Build failed. 45/47 pages pass. Score: 95/100.
Error: Process completed with exit code 1.The pipeline fails. The PR cannot be merged. The developer sees exactly which files caused the issue (app/page.tsx and app/about/page.tsx) and the specific rule violated (og-image-absolute). They restore the metadataBase export, push the fix, the pipeline runs again, and the PR turns green.
This workflow entirely eliminates the class of regressions where marketing discovers broken metadata weeks after a deployment.
How does Indxel validate dynamic Open Graph images?
Indxel sends HTTP HEAD requests to the URLs extracted from og:image tags to verify they return a 200 OK status code and validate their content type.
Dynamic Open Graph images (like Vercel's @vercel/og or Next.js ImageResponse) generate images at runtime based on URL parameters. A common regression occurs when the parameters change, but the metadata function isn't updated, resulting in a 500 Internal Server Error when social platforms attempt to scrape the image.
When the og-image-valid rule is enabled, Indxel does not just check if the tag exists. It parses the content attribute, fires a request to the destination, and validates the response.
// app/blog/[slug]/page.tsx
export async function generateMetadata({ params }) {
return {
title: params.slug,
openGraph: {
// If /api/og throws a 500, Indxel catches it in CI
images: [`/api/og?title=${params.slug}`],
},
}
}If the endpoint fails, Indxel outputs:
error og-image-valid HTTP 500 returned for https://example.com/api/og?title=hello-world
Frequently Asked Questions
Does Indxel check client-rendered metadata?
Yes, Indxel uses a headless browser by default to execute JavaScript and evaluate the final DOM state, catching metadata injected by client-side hooks. If your application relies on React useEffect or Vue's onMounted to append canonical tags, the standard static HTML parsers will miss them. Indxel waits for network idle before evaluating the rules.
How does Indxel handle staging environments behind basic auth?
Pass the --auth flag with your credentials or set the INDXEL_BASIC_AUTH environment variable to authenticate against protected staging URLs.
You run npx indxel crawl https://staging.example.com --auth user:pass. This allows you to validate full deployments in protected Vercel Preview environments before promoting them to production.
Can I ignore specific rules for certain routes?
Add an ignore array in your indxel.config.json specifying the glob patterns and rule IDs you want to bypass.
Admin dashboards or internal API routes do not need strict SEO validation. You can globally ignore paths, or selectively disable rules. For example, {"/admin/**": ["*"], "/blog/**": ["h1-unique"]} ignores all checks on the admin path, and only ignores the duplicate H1 rule on blog posts.
How does Indxel compare to Lighthouse CI?
Indxel is strictly focused on SEO infrastructure with 40+ metadata-specific checks, whereas Lighthouse CI is primarily a performance tool with only 12 basic SEO assertions.
If you are choosing between the two for SEO pipeline gating, Indxel is objectively better. Lighthouse CI checks if a meta description exists; Indxel checks if it exists, if it is between 120-160 characters, and if it duplicates the H1.
| Feature | Indxel | Lighthouse CI |
|---|---|---|
| Primary Focus | SEO Infrastructure | Performance |
| SEO Rules | 40+ configurable rules | 12 basic assertions |
| JSON-LD Validation | Yes, schema.org validation | No |
| Diff Mode (PRs) | Yes, scopes to changed files | No |
| Dynamic OG Check | Yes, HTTP validation | No |
Lighthouse wins on performance budgets and Core Web Vitals tracking. For metadata, canonicals, and indexing directives, Indxel provides the granularity required for production web apps.
npx indxel init