Safeguard
Security Guides•

Next.js server-only, taint and NEXT_PUBLIC: keeping secrets off the client

Three ways to keep private data out of the browser in Next.js: the NEXT_PUBLIC prefix rule, the server-only package and React taint APIs. What each does.

Safeguard Security Team
3 min read

Three things keep secrets out of the browser in Next.js. Environment variables stay on the server unless they start with NEXT_PUBLIC_. Importing the server-only package makes a build fail if a client file pulls the module in. React taint APIs block marked objects from being passed to the client.

The NEXT_PUBLIC_ rule

The Next.js docs state that environment variables are available only on the server by default, and that any variable prefixed with NEXT_PUBLIC_ is exposed to the client. The mistake is naming a real secret with that prefix because a client component needed it. Anything with the prefix should be treated as published.

server-only

Add one import at the top of a module that must never run in the browser:

import 'server-only'

If a client component imports that module, directly or indirectly, the build fails. The docs describe this as keeping proprietary code and internal business logic on the server. They also note Next.js handles the import itself, though you may install the package if your lint rules flag a missing dependency.

Taint APIs

React offers experimental_taintObjectReference for objects and experimental_taintUniqueValue for specific values. You enable them in next.config.js with experimental.taint set to true. A tainted object or value then cannot be passed to the client.

The guide is clear that this is an extra layer. You should still filter and sanitize data in your data access layer before it reaches React's render context. Because the API is experimental, check the current docs before relying on its shape.

What each protects against

  • No NEXT_PUBLIC prefix: Secrets inlined into client bundles
  • server-only: Server modules accidentally imported by client code
  • Taint APIs: Private objects or values passed as props
  • Narrow DTOs: Over-sharing in the first place

Related facts from the guide

  • Server Components run only on the server and can access secrets and databases.
  • Client Components also run on the server during prerendering, but must follow the same assumptions as browser code.
  • Functions and classes are already blocked from being passed to Client Components by default.

A short checklist

  1. Search the repo for NEXT_PUBLIC_ and confirm each value is truly public.
  2. Add server-only to database, auth and key handling modules.
  3. Keep process.env reads inside one server layer.
  4. Review client component props for whole records.

Quick check

Can a NEXT_PUBLIC_ variable hold an API secret?

No. The prefix exposes the value to the client, so use it only for values that are fine to publish.

Is taint enough on its own?

The docs say it is an additional layer. Still filter data before passing it to the client.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.