Your HTML, linted for search.
seolint lints the HTML you build. It finds what search engines trip on.
$ npx seolint public
Get started
One command, every page.
Point it at your build folder. It lints each page, then the whole site. Errors fail the run. Warnings do not.
This is the real output for the example site in the seolint repository.
79 rules, from <title> to the sitemap.
Each rule has its own page in the rules reference. The page says what the rule checks, why it matters, and how to change it.
On every page
Titles, descriptions, and canonical links.The viewport, headings, robots, and images.Links, mixed content, hreflang, Open Graph, and Twitter cards.
Across the whole site
Titles and descriptions that two pages share.Links to files that do not exist.A robots.txt that blocks every crawler, and pages missing from sitemap.xml.
Typed config, your own rules.
Rule names and options are type-checked. Plugins use the same API as the built-in rules.
import * as config from "seolint/config"; import * as presets from "seolint/presets"; export default config.define({ host: "https://example.com", presets: [presets.strict], rules: { "og.*": "warn" }, });
Comments on the pull request.
Run it with --format ci in GitHub, Gitea, or Forgejo Actions.
Each failure shows on its file and line.
Start strict, or start small.
A preset is a config that yours merges in first. You can also share your own across sites.
Fast enough to run on every push.
Measured on 1,200 real pages, 478 MB of HTML, on an Apple M1 Max. Large sites lint in worker threads.
Get started.
seolint needs Node 22.8 or later, or Bun.
- Install it.
npm install --save-dev seolint
- Lint your build.
npx seolint public --host https://example.com
- Add it to CI.
- run: npx seolint public --format ci
Next, follow the quickstart, set up your config, or browse the rules reference.