Why disable GraphQL introspection in production

Introspection is a set of built-in GraphQL meta-fields — __schema and __type — that let any caller ask the API to describe itself: every query, mutation, and subscription field, every custom type, and every field on every type. GraphQL tooling depends on it (schema-aware editors, codegen, GraphiQL/GraphQL Playground), which is exactly why it defaults to on in most server libraries and frameworks — a default nobody has to opt into and few people remember to turn off before shipping.

What it actually hands an attacker

Without introspection, an attacker probing an unfamiliar GraphQL endpoint has to guess field names one at a time, or find them leaked in a bundled client's source. With introspection enabled, they get the entire schema in one request: every mutation (the API's write surface), every type's fields, and every name a developer chose — including internal or debugging-only fields nobody meant to expose, and fields that hint at what a query or mutation does before the attacker has tried it once. This is reconnaissance, not exploitation by itself, but it collapses the first stage of an attack from guesswork to a single request.

Why it's standard practice to turn off, not just a hardening nice-to-have

SourceWhat it says
OWASP API SecurityNames schema introspection in production as a discoverable-surface risk to close
Apollo Server docsDisables introspection by default outside NODE_ENV=development in current versions, after years of the opposite default
graphql-yoga / express-graphqlDocument an explicit flag or plugin to disable introspection in production

Check your own endpoint

The checker on this site sends one real introspection query and reports whether it succeeded, and if so, exactly what it revealed — query, mutation, and subscription field counts, and any field names matching common sensitive-operation wording. It runs directly in your browser when the target's own CORS policy allows it, and ships a zero-dependency CLI for when it doesn't (CORS is a browser-only restriction the CLI is never subject to).

This is evidence from one request, not a full security assessment — see the checker and its disclaimer for what it does and doesn't cover. A sensitive-sounding field name is a hint to review by hand, not proof of a missing authorization check.

← Back to the checker