Signal handling
looks for navigator.globalPrivacyControl / Sec-GPC in inline scripts, the site’s own JS files and edge/serverless functions in the build (functions/, middleware). Do Not Track-only handling is called out.
GitHub Packages · MIT · Node ≥ 18
Check that your built site honours Global Privacy Control: it reads the signal and suppresses sale/share trackers when it is on.
Check your built site honours Global Privacy Control: code reading navigator.globalPrivacyControl / Sec-GPC, sale/share advertising trackers that load unconditionally, /.well-known/gpc.json validity and policy disclosure. CCPA/CPRA, Colorado, Connecticut. SARIF, allowlist with expiry, GitHub Action. Zero deps.
1 Point the @mrlouiswright scope at GitHub Packages in .npmrc
@mrlouiswright:registry=https://npm.pkg.github.com2 Install
npm i -D @mrlouiswright/gpc-wardenPublished 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 gpc-warden dist --base-url https://acme.examplegpc-warden — https://acme.example2 page(s); 2 sale/share vendor(s); GPC handling not found HIGH Sale/share vendors (Meta Pixel, LinkedIn Insight Tag) are used but no code checks navigator.globalPrivacyControl or Sec-GPC [gpc-not-detected] ↳ fix: Read navigator.globalPrivacyControl (and the Sec-GPC request header server-side) and treat it as an opt-out of sale/sharing (CCPA/CPRA, Colorado, Connecticut).HIGH index.html:1 Meta Pixel (script) loads unconditionally on / [gpc-tracker-not-suppressed] ↳ fix: Load it from a script that first checks navigator.globalPrivacyControl (and consent), or tag it for your CMP so it is suppressed when GPC is on.HIGH index.html:1 LinkedIn Insight Tag (pixel) loads unconditionally on / [gpc-tracker-not-suppressed] ↳ fix: Load it from a script that first checks navigator.globalPrivacyControl (and consent), or tag it for your CMP so it is suppressed when GPC is on.MEDIUM .well-known/gpc.json /.well-known/gpc.json: "gpc" must be true or false [gpc-wellknown-invalid]
Global Privacy Control (“Sec-GPC: 1” and navigator.globalPrivacyControl) is a legally binding opt-out of sale and sharing under the CCPA/CPRA and in Colorado, Connecticut and other US states, and California has fined companies for ignoring it. Honouring it means two things: some code has to read the signal, and advertising tags must load in a way that lets that code stop them. gpc-warden checks both in your built site, plus the /.well-known/gpc.json declaration and your privacy policy, and reports each gap with a concrete fix.
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.
looks for navigator.globalPrivacyControl / Sec-GPC in inline scripts, the site’s own JS files and edge/serverless functions in the build (functions/, middleware). Do Not Track-only handling is called out.
advertising vendors that sell or share data (Meta, Google Ads, TikTok, LinkedIn, X, Pinterest, Snap, Microsoft, Criteo, Taboola, AddThis, ...) must not load unconditionally: they need a GPC-aware loader or CMP tagging (type="text/plain" + category).
OneTrust, Osano, Usercentrics, Didomi, Termly, Ketch, TrustArc, Transcend can honour GPC; confirm it is enabled with --cmp-honours-gpc.
present, valid ({"gpc": true, "lastUpdate": "YYYY-MM-DD"}) and not false while selling/sharing.
the privacy policy mentions GPC / opt-out preference signals.
# Example
npx gpc-warden dist# Example
npx gpc-warden dist --fail-on medium# Example
npx gpc-warden public --cmp-honours-gpc# Example
npx gpc-warden out --format sarif > gpc.sarifpermissions:
contents: read
security-events: write # only needed for the SARIF upload
jobs:
gpc-warden:
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/gpc-warden@v1
with:
path: dist
fail-on: high
sarif: gpc-warden.sarif
- uses: github/codeql-action/upload-sarif@v3
if: always()
with: { sarif_file: gpc-warden.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) |
| GPC | |
--cmp-honours-gpc | Your CMP (OneTrust, Osano, Usercentrics, ...) is configured to honour GPC |
| Common | |
-c, --config <file> | JSON config (default: .gpc-warden.json or "gpcWarden" 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