Mockly

glossary

NEXT_PUBLIC Secret Leakage

Any secret placed in a NEXT_PUBLIC variable becomes public, so these env vars can never hold sensitive keys. This page explains it in plain English, then goes deeper into how it works in Supabase/Postgres, what commonly goes wrong, and how to fix it without relying on fragile client-side rules.

What “NEXT_PUBLIC Secret Leakage” means (plain English)

Next.js exposes NEXT_PUBLIC_* variables to the client build, meaning they are embedded in shipped JavaScript.

How NEXT_PUBLIC Secret Leakage works in Supabase/Postgres (technical)

Because the build pipeline includes NEXT_PUBLIC keys in client bundles, storing secrets there exposes them to anyone who downloads your assets.

Attack paths & failure modes for NEXT_PUBLIC Secret Leakage

  • NEXT_PUBLIC env exposes a secret: A developer needs a quick fix and sets a secret in .env.local as NEXT_PUBLIC_* so the client component can read it.
  • Server secret imported into a client component: A shared helper for supabaseAdminClient ends up imported by a client component, directly or through another module.
  • NEXT_PUBLIC env exposes a secret: Next.js exposes all NEXT_PUBLIC_* variables to the client, so the secret ended up in shipped JavaScript and was easily extracted.
  • Server secret imported into a client component: The dependency graph touched a client file, so the secret-bearing module was bundled and shipped.
  • Secrets are placed in NEXT_PUBLIC_* env vars and embedded into shipped JavaScript by design.
  • Server-only secret modules are imported into client components (directly or indirectly), causing accidental bundling.
  • Debug logging or error reporting includes secrets and persists them in third-party systems.
  • Secrets aren’t rotated after exposure, so old bundles remain permanently exploitable.

Why NEXT_PUBLIC Secret Leakage matters for Supabase security

A leaked secret lets attackers bypass RLS and backend protections, so the attack surface includes every asset you ship. If NEXT_PUBLIC Secret Leakage remains unresolved, attackers can automate enumeration and unauthorized writes at API speed. Treat it as a production reliability risk as well as a data security risk, because incidents spread quickly once clients discover weak access boundaries.

Common NEXT_PUBLIC Secret Leakage mistakes that lead to leaks

  • Setting secrets as NEXT_PUBLIC for convenience.
  • Importing server secret modules into client components by accident.
  • Assuming build-time scanning will catch every leak.
  • NEXT_PUBLIC env exposes a secret: Next.js exposes all NEXT_PUBLIC_* variables to the client, so the secret ended up in shipped JavaScript and was easily extracted.
  • Server secret imported into a client component: The dependency graph touched a client file, so the secret-bearing module was bundled and shipped.

Where to look for NEXT_PUBLIC Secret Leakage in Supabase

  • Any NEXT_PUBLIC_ variables: confirm none contain secrets or privileged keys.
  • Import graphs from client components to ensure they don’t pull in server-only secret modules.
  • Build artifacts: scan for secret-like values and known key prefixes in client output.

How to detect NEXT_PUBLIC Secret Leakage issues (signals + checks)

Use this as a quick checklist to validate your current state:

  • Try the same queries your frontend can run (anon/authenticated). If sensitive rows come back, you have exposure.
  • Verify RLS is enabled and (for sensitive tables) forced.
  • List policies and look for conditions that don’t bind rows to a user or tenant.
  • Audit grants to anon / authenticated on sensitive tables and functions.
  • NEXT_PUBLIC env exposes a secret: NEXT_PUBLIC means public by design.
  • NEXT_PUBLIC env exposes a secret: Client bundles are attacker-readable.
  • NEXT_PUBLIC env exposes a secret: Rotate secrets immediately after suspected exposure.
  • Re-test after every migration that touches security-critical tables or functions.

How to fix NEXT_PUBLIC Secret Leakage (backend-only + zero-policy posture)

Mockly’s safest default is backend-only access: the browser should not query tables, call RPC, or access Storage directly.

  1. Decide which operations must remain client-side (often: none for sensitive resources).
  2. Create server endpoints (API routes or server actions) for required reads/writes.
  3. Apply hardening SQL: enable+force RLS where relevant, remove broad policies, and revoke grants from client roles.
  4. Generate signed URLs for private Storage downloads on the server only.
  5. Re-run a scan and confirm the issue disappears.
  6. Add a regression check to your release process so drift doesn’t reintroduce exposure. Fixes that worked in linked incidents:
  • NEXT_PUBLIC env exposes a secret: Move the secret to a server-only env var, rotate it if compromised, and route privileged operations through backend endpoints.
  • Server secret imported into a client component: Enforce server-only module boundaries, refactor client code to call backend endpoints, and rotate any leaked secrets.

Verification checklist for NEXT_PUBLIC Secret Leakage

  1. Search built client assets for sensitive values and key patterns (treat as public).
  2. Rotate any secret that may have shipped and update server environments.
  3. Enforce a server-only Supabase admin client and route privileged operations through backend endpoints.
  4. Add a guardrail so future builds fail if secrets appear in client output.
  5. NEXT_PUBLIC env exposes a secret: NEXT_PUBLIC means public by design.
  6. NEXT_PUBLIC env exposes a secret: Client bundles are attacker-readable.
  7. NEXT_PUBLIC env exposes a secret: Rotate secrets immediately after suspected exposure.
  8. NEXT_PUBLIC env exposes a secret: Backend endpoints remove the need for client secrets.

SQL sanity checks for NEXT_PUBLIC Secret Leakage (optional, but high signal)

If you prefer evidence over intuition, run a small set of SQL checks after each fix.

The goal is not to memorize catalog tables — it’s to make sure the access boundary you intended is the one Postgres actually enforces:

  • Confirm RLS is enabled (and forced where appropriate) for tables tied to this term.
  • List policies and read them as plain language: who can do what, under what condition?
  • Audit grants for anon/authenticated and PUBLIC on the tables, views, and functions involved.
  • If Storage is involved: review bucket privacy and policies for listing/reads.
  • If RPC is involved: review EXECUTE grants for functions and whether privileged functions are server-only.

Pair these checks with a direct API access test using client credentials. When both agree, you can ship the fix with confidence.

Over time, keep a small “query pack” for the checks you trust and run it after every migration. That’s how you prevent quiet regressions.

Prevent NEXT_PUBLIC Secret Leakage drift (so it doesn’t come back)

  • Adopt a policy: secrets are server-only; NEXT_PUBLIC_* is never used for privileged credentials.
  • Introduce tooling (lint/build checks) to prevent server-only modules from being imported by client code.
  • Redact secrets in logs and error reporting, and rotate on suspicion.
  • Keep one reusable verification test for “NEXT_PUBLIC env exposes a secret” and rerun it after every migration that touches this surface.
  • Keep one reusable verification test for “Server secret imported into a client component” and rerun it after every migration that touches this surface.

Rollout plan for NEXT_PUBLIC Secret Leakage fixes (without breaking production)

Many hardening changes fail because teams revoke direct access first and only later discover missing backend paths.

Use this sequence to reduce both risk and outage pressure:

  1. Implement and verify the backend endpoint or server action before permission changes.
  2. Switch clients to that backend path behind a feature flag when possible.
  3. Then revoke direct client access (broad grants, permissive policies, public bucket reads, or broad EXECUTE).
  4. Run direct-access denial tests and confirm authorized backend flows still succeed.
  5. Re-scan after deployment and again after the next migration.

This turns security fixes into repeatable rollout mechanics instead of one-off emergency changes.

Incident breakdowns for NEXT_PUBLIC Secret Leakage (real scenarios)

NEXT_PUBLIC env exposes a secret

Scenario: A developer needs a quick fix and sets a secret in .env.local as NEXT_PUBLIC_* so the client component can read it.

What failed: Next.js exposes all NEXT_PUBLIC_* variables to the client, so the secret ended up in shipped JavaScript and was easily extracted.

What fixed it: Move the secret to a server-only env var, rotate it if compromised, and route privileged operations through backend endpoints.

Why the fix worked: The secret never appears in client assets again and rotating invalidates any previously extracted values.

Key takeaways:

  • NEXT_PUBLIC means public by design.
  • Client bundles are attacker-readable.
  • Rotate secrets immediately after suspected exposure.
  • Backend endpoints remove the need for client secrets.

Read full example: NEXT_PUBLIC env exposes a secret

Server secret imported into a client component

Scenario: A shared helper for supabaseAdminClient ends up imported by a client component, directly or through another module.

What failed: The dependency graph touched a client file, so the secret-bearing module was bundled and shipped.

What fixed it: Enforce server-only module boundaries, refactor client code to call backend endpoints, and rotate any leaked secrets.

Why the fix worked: Clear module boundaries keep secrets on the server and backend endpoints provide a reviewable interface for privileged work.

Key takeaways:

  • Client/server boundaries break easily.
  • Secrets can leak via indirect imports.
  • Prefer backend endpoints over client-side admin helpers.
  • Add build checks to prevent regressions.

Read full example: Server secret imported into a client component

Real-world examples of NEXT_PUBLIC Secret Leakage (and why they work)

Related terms

  • Service Role Key → /glossary/service-role-key
  • Client Role Grants (anon/authenticated) → /glossary/client-role-grants

FAQ

Is NEXT_PUBLIC Secret Leakage enough to secure my Supabase app?

It’s necessary, but not sufficient. You also need correct grants, secure Storage/RPC settings, and a backend-only access model for sensitive operations.

What’s the quickest way to reduce risk with NEXT_PUBLIC Secret Leakage?

Remove direct client access to sensitive resources, enable/force RLS where appropriate, and verify via a repeatable checklist that anon/authenticated cannot query what they shouldn’t.

How do I verify the fix is real (not just a UI change)?

Attempt direct API queries using the same client credentials your app ships. If the database denies access (401/403) and your backend endpoints still work, your fix is effective.

Next step

Want a quick exposure report for your own project? Run a scan in Mockly to find public tables, storage buckets, and RPC functions — then apply fixes with verification steps.

Explore related pages

parent

Glossary

/glossary

sibling

Client Role Grants (anon/authenticated)

/glossary/client-role-grants

sibling

Service Role Key

/glossary/service-role-key

cross

Next.js backend-only Supabase access

/integrations/nextjs-backend-only-supabase

cross

NEXT_PUBLIC env exposes a secret

/examples/next-public-secret-leakage/next-public-env-exposes-secret

cross

Pricing

/pricing