I once spent an entire afternoon debugging why Google couldn't see my React app. The fix was 4 lines of code: a missing robots meta tag was quietly blocking the whole /blog route. That bug taught me something: SEO isn't a marketing problem, it's an engineering problem, and there's a real open source SEO tools stack for solving it, no paid dashboard required.
Quick answer: the best open source SEO tools for developers, by job:
- Lighthouse CI: official Google tool, runs SEO/perf/a11y audits per PR
- Unlighthouse: runs Lighthouse across your entire site, not just one URL
- broken-link-checker: crawls a site for dead links
- sitemap: generates XML sitemaps from your actual routes
- schema-dts + a JSON-LD validator: catches broken structured data
- power-seo: bundles checks like the above into one CI command
Here's how each one actually works, with code.
Best Open Source SEO Tools at a Glance
If you're comparing open source SEO tools for a CI setup, here's how these six stack up:
| Tool | Checks | Runs As | Best For |
|---|---|---|---|
| Lighthouse CI | Perf, SEO, a11y score | CLI / GitHub Action | Auditing one URL on every PR |
| Unlighthouse | Same, site-wide | CLI | Catching issues on pages nobody remembers to test |
| broken-link-checker | Dead internal/external links | Node CLI/library | Link rot before Google finds it |
| sitemap | XML sitemap generation | Node library | Keeping sitemap.xml in sync with routes |
| schema-dts + validator | JSON-LD correctness | TS types + script | Structured data that silently breaks |
| power-seo | Bundles the above | CLI | One command, one CI step |
1. Lighthouse CI: the Official, Battle-Tested Auditor
Lighthouse is the engine behind Chrome DevTools' audit tab, and it's fully open source. Lighthouse CI wraps it so you can fail a build on score regressions instead of eyeballing a report once a quarter.
npm install -g @lhci/cli
# lighthouserc.js
module.exports = {
ci: {
collect: {
url: ['http://localhost:3000/', 'http://localhost:3000/blog'],
numberOfRuns: 3,
},
assert: {
assertions: {
'categories:seo': ['error', { minScore: 0.9 }],
},
},
upload: { target: 'temporary-public-storage' },
},
};
lhci autorun
Result: the run fails with a non-zero exit code the moment SEO score drops below 90. For example, a deploy that accidentally strips <meta name="viewport"> gets caught before merge, not after a client complains.
2. Unlighthouse: Auditing Every Page, Not Just the Homepage
Lighthouse CI is great, but you have to list every URL by hand. Unlighthouse crawls your live site and runs Lighthouse against every discovered page automatically, which is how you catch the one blog post from 2023 nobody's checked since.
npx unlighthouse --site https://yoursite.com
That's it, no config file required for a first pass. It opens a local dashboard showing SEO/perf/a11y scores for every crawled URL, and you can pass --budget flags to fail CI on a site-wide threshold instead of a single page.
Result: on a 40-page marketing site, this surfaced 6 pages missing canonical tags that Lighthouse CI's manual URL list had simply never included.
3. broken-link-checker: Catching Link Rot Before Google Does
Broken internal links waste crawl budget and tank UX; broken outbound links quietly signal a stale, low-effort page. broken-link-checker crawls a site (or a single HTML string) and reports every dead link.
npm install broken-link-checker --save-dev
// check-links.js
const { SiteChecker } = require('broken-link-checker');
const checker = new SiteChecker(
{ excludeExternalLinks: false },
{
link: (result) => {
if (result.broken) {
console.warn(`❌ Broken: ${result.url.original} (found on ${result.base.original})`);
}
},
end: () => console.log('✅ Link check complete'),
}
);
checker.enqueue('https://yoursite.com');
Result: this caught three internal links pointing at a renamed /docs/v1/ path six months after a docs restructure. Search Console wouldn't have flagged it for weeks.
4. sitemap: Keeping Your Sitemap From Going Stale
Hand-written sitemaps rot. Generate the XML from your actual route list at build time so it can never drift.
npm install sitemap --save-dev
// generate-sitemap.js
const { SitemapStream, streamToPromise } = require('sitemap');
const { createWriteStream } = require('fs');
const routes = require('./routes.json'); // your actual route list
async function generateSitemap() {
const sitemap = new SitemapStream({ hostname: 'https://yoursite.com' });
const writeStream = createWriteStream('./public/sitemap.xml');
sitemap.pipe(writeStream);
routes.forEach((route) => {
sitemap.write({
url: route.path,
changefreq: route.changefreq || 'weekly',
priority: route.priority || 0.5,
lastmod: route.lastModified,
});
});
sitemap.end();
await streamToPromise(sitemap);
console.log(`✅ Sitemap generated with ${routes.length} URLs`);
}
generateSitemap();
Wire it into "prebuild": "node generate-sitemap.js" in package.json.
Result: caught 14 orphaned URLs still submitted to Search Console months after the pages were removed, pure crawl-budget waste.
5. schema-dts + a JSON-LD Validator: Structured Data That Doesn't Silently Break
Broken JSON-LD doesn't throw a JS error or break your UI. It just fails to earn rich results. schema-dts gives you TypeScript types for schema.org so a missing required field is a compile error, not a runtime mystery. Pair it with a runtime check for anything rendered outside TypeScript's reach:
npm install schema-dts --save-dev
npm install jsdom --save-dev
// validate-structured-data.js
const { JSDOM } = require('jsdom');
function validateJsonLd(html) {
const dom = new JSDOM(html);
const scripts = dom.window.document.querySelectorAll('script[type="application/ld+json"]');
if (scripts.length === 0) {
console.warn('⚠️ No structured data found on this page');
return;
}
scripts.forEach((script, i) => {
try {
const data = JSON.parse(script.textContent);
if (!data['@type']) console.warn(`⚠️ Block ${i}: missing @type`);
else console.log(`✅ Block ${i}: valid @type "${data['@type']}"`);
} catch (e) {
console.error(`❌ Block ${i}: invalid JSON: ${e.message}`);
}
});
}
const html = require('fs').readFileSync('./rendered-page.html', 'utf8');
validateJsonLd(html);
For a spec-complete check, cross-reference against Google's Rich Results Test too. This script catches malformed JSON, not every schema.org requirement.
Result: caught a template outputting undefined into a JSON-LD block when a product's price field was missing, silently breaking the whole block, not just that field.
6. power-seo: Bundling All of the Above Into One CI Step
Running five separate scripts works until someone forgets to run one. power-seo bundles checks like the ones above (missing meta tags, duplicate titles, broken canonicals, orphaned sitemap entries) into a single CLI command for CI.
# .github/workflows/seo-check.yml
name: SEO Audit
on: [pull_request]
jobs:
seo-audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm install
- run: npx power-seo audit --url http://localhost:3000 --fail-on-warning
Disclosure: I work with the team behind this package, so take the recommendation with that context. The code above is real, MIT-licensed, and you can read every check it runs before trusting it with your build. The value isn't the tool itself, it's that the check runs on every PR instead of when someone remembers. You could wire the same pipeline yourself from tools 1–5; power-seo just saves the glue code. Longer writeup on the reasoning is on ccbd.dev if you want it.
What I Learned
- Single-page tools (Lighthouse CI) and site-wide crawlers (Unlighthouse) catch different bugs, so use both, not one instead of the other.
- Link rot and broken structured data are both silent failures. Neither throws an error; both need a dedicated check.
- Automate the sitemap. A hand-maintained one is a lie waiting to happen.
- None of this replaces off-page tools (backlinks, keyword volume). It just closes the technical-SEO gaps that are 100% within a developer's control.
- If you only adopt one thing from this list, make it CI enforcement, that's what turns "open source SEO tools" from a bookmarked list into something that actually protects your rankings.
If you want to try the bundled CI approach, here's the repo: https://github.com/CyberCraftBD/power-seo
Let's Talk
Which of these have you actually used, and which open source SEO tool should've been on this list? I'll start: still looking for a good open source alternative to Screaming Frog's UI-based crawl reports. Drop your pick below.
Top comments (0)