Audit any Payload CMS instance for the misconfigurations that actually leak data — collections readable without auth, field-level leaks (
apiKey/hash), user/login enumeration, open first-user registration, and GraphQL introspection — and prove each one live with an anonymous probe. Other checklists tell you what might be wrong; this fetches the bytes and shows you what is.
⚡ Run it in one line, no admin token, no install:
npx payload-security --url https://your-payload.example.com
🤝 Want it done for you? Fixed-scope audit — $99 / 24h: I verify each finding live and send a written report with the exact access-control fixes.
$ npx payload-security --url https://cms.example.com
1 critical, 1 high, 1 medium — 3 CONFIRMED via anonymous probe
CRITICAL /api/posts readable without auth — 128 docs reachable
CRITICAL /api/users leaks apiKey, email to anonymous reads
MEDIUM /api/graphql GraphQL introspection enabled in production
Payload is one of the fastest-growing TypeScript-native headless CMSs (now part
of Vercel) — and it is not secure-by-default. Every collection's
access.read must be set explicitly; the REST API at /api/{slug} and GraphQL
at /api/graphql are exposed automatically. Payload's own docs warn that
local-API operations override access control
and that custom endpoints are not authenticated by default.
The widely-shared 2026 write-up
Payload CMS Security Best Practices: Top 10 Threats & Mitigation Strategies
(u11d) puts it bluntly: "attackers scan APIs first, not your frontend" — and a
huge share of Payload projects ship without proper read access on their
collections.
The endpoint shape is predictable (/api/{kebab-slug}, /api/graphql), so
proving a leak anonymously is trivial. payload-security runs those checks and
confirms the real ones by issuing the exact unauthenticated request an
attacker would — so you triage facts, not maybes.
| Check | Severity | How it's confirmed |
|---|---|---|
| Collection readable without auth | critical | anonymous GET /api/{slug} returns docs |
Sensitive field leak (apiKey/email/hash/salt) |
critical | a returned doc contains the field with a value |
/api/users auth-collection enumeration |
high | anonymous GET /api/users returns user docs |
| Open first-user registration | high | /api/users reachable and reports totalDocs:0 |
| GraphQL introspection in prod | medium | __schema query answered at /api/graphql |
| GraphQL playground in prod | medium | /api/graphql-playground served live |
# Probe a live instance (guesses common collection slugs)
npx payload-security --url https://cms.example.com
# Learn your exact collection slugs from your local repo, then probe
npx payload-security --url https://cms.example.com --discover ./my-payload-app
# Probe specific collections
npx payload-security --url https://cms.example.com --collections posts,media
# Write a shareable HTML report
npx payload-security --url https://cms.example.com --html report.html
# Static only (no requests sent)
npx payload-security --url https://cms.example.com --no-probeOutput is JSON on stdout (pipe it into CI) and a one-line summary on stderr.
Exit is non-zero only on usage errors — gate your pipeline on the JSON summary.
npm i -g payload-security
payload-security --url https://cms.example.comZero dependencies. Your data and credentials never leave your machine — every request goes straight from the tool to your Payload instance.
Same active-probe philosophy for the rest of the backend stack, all MIT:
strapi-security · directus-security · supabase-security · pocketbase-security · firebase-security · appwrite-security · nhost-security · convex-security
MIT © Renzo Madueno
📚 Part of Awesome Backend Security Auditors — the full collection of keyless active-probe auditors.