Fix guide · medium · graphql_playground_exposed

GraphQL Playground / GraphiQL UI exposed in production

What this rule means

A GraphQL Playground, GraphiQL, or Apollo Sandbox UI is reachable on a production endpoint. The UI is a full interactive query editor against your live schema — useful for development, dangerous to ship.

Why it matters

GraphQL Playground (and its successors GraphiQL, Apollo Sandbox, and Apollo Studio Explorer) is the in-browser query editor that ships with most GraphQL servers. Hit /graphql in a browser and you get a fully-featured IDE with:

In development this is invaluable. In production it's a free reconnaissance tool for attackers. Even if you've disabled introspection, the Playground UI itself loads from a CDN and presents your endpoint URL prominently, with examples — meaning even hostile inspection of your traffic doesn't have to discover the endpoint, you've put it on a sign.

Common offenders shipping the Playground to production:

Disabling the Playground does NOT disable introspection — that's a separate flag (see /fix/graphql_introspection_enabled). Best practice is to disable both in production.

How to fix it

Apollo Server v3:

const server = new ApolloServer({
  typeDefs, resolvers,
  introspection: false,
  plugins: [
    process.env.NODE_ENV === 'production'
      ? ApolloServerPluginLandingPageDisabled()
      : ApolloServerPluginLandingPageLocalDefault(),
  ],
});

Apollo Server v4:

const server = new ApolloServer({
  typeDefs, resolvers,
  introspection: process.env.NODE_ENV !== 'production',
});
// Landing page is disabled in production by default in v4.

GraphQL Yoga:

import { createYoga } from 'graphql-yoga';
const yoga = createYoga({
  schema,
  graphiql: process.env.NODE_ENV !== 'production',
});

Express-GraphQL:

app.use('/graphql', graphqlHTTP({
  schema,
  graphiql: process.env.NODE_ENV !== 'production',
}));

Hasura:

HASURA_GRAPHQL_ENABLE_CONSOLE: 'false'

Defense in depth: even with the Playground disabled, also disable introspection (see /fix/graphql_introspection_enabled) and ensure every resolver enforces its own auth. Schema-hiding is reconnaissance defence, not authorisation defence.

Did vibecheck flag this on your app?

If you reached this page from a vibecheck inspection report, the redacted match in your scan output is the exact string we found in your bundle. After applying the fix above, run the inspection again — the finding should clear.

Run another inspection