GitHub Packages · MIT · Node ≥ 18

meta-beacon

Audit titles, meta descriptions, Open Graph and Twitter/X cards across a built static site.

  1. 1 Point the @mrlouiswright scope at GitHub Packages in .npmrc

    @mrlouiswright:registry=https://npm.pkg.github.com
  2. 2 Install

    npm i -D @mrlouiswright/meta-beacon
Installing from GitHub Packages needs a token

Published 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.

~/project — zsh
$ 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).
Sample output from the meta-beacon README.

Why it exists

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.

Features

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).

Open Graph

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.

Real share images

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/X cards

twitter:card is present and valid (summary, summary_large_image, app or player).

Head basics

<meta charset> within the first 1024 bytes, viewport, and <html lang>.

Output

text, JSON, SARIF 2.1.0 and Markdown; --fail-on; exit codes 0/1/2; allowlist entries with expiry dates.

Usage

# 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-twitter

CI / GitHub Action

permissions:
  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 }

Options & flags

FlagDescription
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-urlsPages 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-ogDo not require Open Graph tags
--no-twitterDo 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-rulesPrint rules and default severities
--no-fixesHide fix suggestions in text output
--no-colorDisable colours

Full reference, config file format and API in the README.

Support this project

Support open-source security tools

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

Portrait of Louis Wright

About the author

Louis Wright

Cyber security researcher and software engineer based in the UK.

More tools by Louis Wright

58 tools

Sentinels security scanners & CI gates · 20