Discovery
sitemap.xml, sitemap_index.xml, sitemap-*.xml, .xml.gz, and the files named in Sitemap: lines in robots.txt. Sitemap indexes are followed to their children, and missing children are reported.
GitHub Packages · MIT · Node ≥ 18
Audit XML sitemaps against your built static site: every URL should be a real, indexable, canonical page, and every such page should be listed.
1 Point the @mrlouiswright scope at GitHub Packages in .npmrc
@mrlouiswright:registry=https://npm.pkg.github.com2 Install
npm i -D @mrlouiswright/sitemap-beaconPublished to GitHub Packages, not npmjs.com. GitHub Packages needs a GitHub token with read:packages to install, even for public packages: add //npm.pkg.github.com/:_authToken=${GITHUB_TOKEN} to .npmrc or run npm login --registry=https://npm.pkg.github.com.
$ npx sitemap-beacon dist --base-url https://example.com --fail-on mediumsitemap-beacon — https://example.com8 page(s); 2 sitemap file(s) with 10 URL(s) on https://example.com HIGH sitemap_index.xml:4 sitemap_index.xml lists sitemap-posts.xml, which is not in the build [sitemap-child-missing] ↳ fix: Generate the child sitemap or remove it from the index.HIGH sitemap-pages.xml:6 <loc>/blog/copy.html</loc> is not an absolute http(s) URL [sitemap-loc-invalid] ↳ fix: Use the full URL, e.g. https://example.com/blog/copy.html.HIGH sitemap-pages.xml:9 https://example.com/private/secret.html is noindex (X-Robots-Tag in _headers) [sitemap-url-noindex] ↳ fix: Remove noindex pages from the sitemap, or remove the noindex.HIGH sitemap-pages.xml:10 https://example.com/tag.html is noindex (<meta name="robots">) [sitemap-url-noindex] ↳ fix: Remove noindex pages from the sitemap, or remove the noindex.HIGH sitemap-pages.xml:11 https://example.com/old.html is a meta-refresh redirect page (old.html) [sitemap-url-non-200] ↳ fix: List the destination URL instead.... 8 high, 5 medium, 5 low, 1 info✔ Passing (fail-on: none).
Sitemaps drift away from the site they describe. A page gets noindex but stays in the sitemap. A post is canonicalised to its new URL, but the old one is still listed. The build stamps every lastmod with the deploy time, so Google stops trusting the dates. A section is added and never makes it into the sitemap. sitemap-beacon reads your sitemaps (indexes, children and .xml.gz) alongside the built HTML and checks each one against the other. It reports the file, line, severity and a concrete fix, before you deploy.
CLI, library and GitHub Action. Node.js 18+, zero runtime dependencies. Works on any static build output (Astro, Next.js “output: 'export'”, Eleventy, Hugo, Jekyll, Docusaurus, VitePress, plain HTML) and runs offline: point it at the folder your build writes. By Louis Wright.
sitemap.xml, sitemap_index.xml, sitemap-*.xml, .xml.gz, and the files named in Sitemap: lines in robots.txt. Sitemap indexes are followed to their children, and missing children are reported.
valid <urlset>/<sitemapindex> root and namespace, 50,000-URL and 50 MB limits, absolute and properly escaped <loc>, duplicates, and valid changefreq/priority (which Google ignores; reported as info).
no page at that URL (or only a meta-refresh stub or an error page), noindex from meta robots or from X-Robots-Tag in _headers/vercel.json, canonicalised to a different URL, or another host or protocol.
invalid W3C datetime, in the future, older than the page's own dateModified/article:modified_time, the same on every URL (a build timestamp), and an optional maximum age.
indexable, self-canonical pages that are not in any sitemap. Use notInSitemap globs for pages you leave out on purpose.
text, JSON, SARIF 2.1.0 and Markdown; --fail-on; exit codes 0/1/2; allowlist entries with expiry dates.
# Example
npx sitemap-beacon dist# Example
npx sitemap-beacon dist --base-url https://example.com --fail-on medium# Example
npx sitemap-beacon public --sitemap sitemap-index.xml --max-age-days 730# Example
npx sitemap-beacon out --clean-urls --not-in-sitemap "/tags/**,/page/*"permissions:
contents: read
security-events: write # only needed for the SARIF upload
jobs:
sitemap-beacon:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with: { persist-credentials: false }
- run: npm ci && npm run build # produce the static site first
- uses: MrLouisWright/sitemap-beacon@v1
with:
path: dist
fail-on: high
sarif: sitemap-beacon.sarif
- uses: github/codeql-action/upload-sarif@v3
if: always()
with: { sarif_file: sitemap-beacon.sarif }
| Flag | Description |
|---|---|
| Site | |
--base-url <url> | Public URL of the site, e.g. https://example.com (default: inferred from canonical/og:url) |
--include <globs> | Only check these pages (file or URL path globs, comma-separated) |
--exclude <globs> | Skip these pages |
--clean-urls | Pages are served without .html (about.html -> /about) |
| Checks | |
--sitemap <files> | Sitemap file(s) relative to the site dir (default: auto-detect) |
--max-age-days <n> | Report lastmod older than n days (info) |
--no-coverage | Do not report pages missing from the sitemap |
| Common | |
-c, --config <file> | JSON config (default: .sitemap-beacon.json or "sitemapBeacon" in package.json) |
-f, --format <fmt> | text (default) | json | sarif | markdown |
--json <file> | Also write a JSON report |
--sarif <file> | Also write SARIF 2.1.0 |
--markdown <file> | Also write Markdown (e.g. for a job summary) |
--fail-on <sev> | critical | high | medium | low | info | none (default: high) |
--list-rules | Print rules and default severities |
--no-fixes | Hide fix suggestions in text output |
--no-color | Disable colours |
Full reference, config file format and API in the README.
Support this project
I build free, open-source tools that help developers ship safer websites. Every coffee helps keep them maintained, documented and free for everyone.
Buy me a coffee (opens Stripe in a new tab)Secure checkout by Stripe