Titles and descriptions
missing, empty, duplicated in the head, placed in <body>, and length outside a configurable range (default titles 10–60 and descriptions 50–160 characters).
GitHub Packages · MIT · Node ≥ 18
Audit titles, meta descriptions, Open Graph and Twitter/X cards across a built static site.
1 Point the @mrlouiswright scope at GitHub Packages in .npmrc
@mrlouiswright:registry=https://npm.pkg.github.com2 Install
npm i -D @mrlouiswright/meta-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 meta-beacon dist --base-url https://example.com --fail-on mediummeta-beacon — https://example.com4 page(s) at https://example.com (inferred) HIGH blog/post.html:11 og:image https://example.com/img/missing.jpg is not in the build (/img/missing.jpg) [og-image-not-found] ↳ fix: Fix the path or add the image to the build output; shares will show no image.MEDIUM blog/post.html:6 meta description is empty [description-missing] ↳ fix: Add <meta name="description" content="…"> summarising the page in one or two sentences.MEDIUM blog/post.html:12 /img/logo.svg is SVG [og-image-format] ↳ fix: Use a PNG or JPEG (WebP works on most platforms).MEDIUM blog/post.html:14 <title> is inside <body> [meta-outside-head] ↳ fix: Move <title> into <head> (an element before it may have closed the head early).MEDIUM blog/tiny.html:4 /img/tiny.png is 100×100; Facebook and LinkedIn ignore images under 200×200 [og-image-size] ↳ fix: Use 1200×630 (1.91:1).... 1 high, 9 medium, 9 low, 1 info✔ Passing (fail-on: none).
Every page needs a good title, a description and a share preview, and they break in small ways. A template change drops “og:image” from blog posts. A CMS emits two “<title>” tags. Someone ships “og:image="/img/share.png"”, but scrapers don't resolve relative URLs, so links shared on LinkedIn show no image. The share image is an SVG that Facebook won't render, or 300 px wide when 1200×630 is expected. meta-beacon reads every page in your build output and reports each of these problems with the file, line, severity and a concrete fix. It opens the real image files to check their format and dimensions.
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.
missing, empty, duplicated in the head, placed in <body>, and length outside a configurable range (default titles 10–60 and descriptions 50–160 characters).
og:title, og:type, og:image and og:url are present; URLs are absolute; og:url matches the canonical URL; name="og:*" vs property= mix-ups; og:image:alt.
og:image and twitter:image on your own domain are resolved to files in the build. Missing files, SVG/AVIF, images under 200×200 (ignored by Facebook and LinkedIn), large-card images under 1200×630 and files over 5 MB are reported. PNG, JPEG, GIF and WebP headers are read directly.
twitter:card is present and valid (summary, summary_large_image, app or player).
<meta charset> within the first 1024 bytes, viewport, and <html lang>.
text, JSON, SARIF 2.1.0 and Markdown; --fail-on; exit codes 0/1/2; allowlist entries with expiry dates.
# after your build
npx meta-beacon dist# Example
npx meta-beacon dist --base-url https://example.com --fail-on medium# Example
npx meta-beacon _site --title-length 15-65 --description-length 70-155# Example
npx meta-beacon out --exclude "drafts/**" --no-twitterpermissions:
contents: read
security-events: write # only needed for the SARIF upload
jobs:
meta-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/meta-beacon@v1
with:
path: dist
fail-on: high
sarif: meta-beacon.sarif
- uses: github/codeql-action/upload-sarif@v3
if: always()
with: { sarif_file: meta-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 | |
--title-length <min-max> | Title length range in characters (default 10-60) |
--description-length <min-max> | Description length range (default 50-160) |
--no-og | Do not require Open Graph tags |
--no-twitter | Do not require twitter:card |
| Common | |
-c, --config <file> | JSON config (default: .meta-beacon.json or "metaBeacon" 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