The Hidden Header: Next.js Next-Router-State-Tree and Why It Matters

When working with frameworks like Next.js, developers often expect that certain internal mechanisms remain invisible to the outside world. Unfortunately, that’s not always the case. Recently, while working on dele.to (an open-source project for sharing sensitive data securely), a security researcher discovered that URL fragments where sent to the network request via header called Next-Router-State-Tree.
At first glance, it looks harmless, just part of Next.js’ internal routing logic. But under the hood, it can carry more than you would expect.
What is Next-Router-State-Tree?
Next.js uses the Next-Router-State-Tree header to pass routing state between server and client for navigation and hydration. In theory, it is meant for internal use only. In practice, it is leaked in network requests and visible in server logs, proxies, or observability tools.
Why This Is a Problem
URL Fragments and Query Data
The header may contain the full requested URL, including fragments (#hash) or sensitive query params. Normally, fragments never leave the browser, but here they get serialized into this header, meaning data you thought was client-only can unexpectedly travel across the wire.Logs and Monitoring
If your app is deployed behind a reverse proxy, CDN, or observability platform, this header is often logged. Suddenly, internal state, routing data, or sensitive identifiers become part of access logs you do not control.Attack Surface
Attackers could potentially harvest extra information from leaked headers if they gain access to logs or intercept requests. Even if the data is not outright sensitive, it adds unnecessary exposure.
Example from dele.to
Here is an example link from dele.to:
https://dele.to/view/066d161a-5744-4904-8da3-bb7cbb3e07ba#wBPa77IW3/lKG/9dYGkke55cWGncciYqiKm8wJzYZMo=
next-router-state-tree looks like this:
%5B%22%22%2C%7B%22children%22%3A%5B%22view%22%2C%7B%22children%22%3A%5B%5B%22id%22%2C%221b0c4494-0899-4bb5-b6c9-66cdbbc23ad0%22%2C%22d%22%5D%2C%7B%22children%22%3A%5B%22__PAGE__%22%2C%7B%7D%2C%22%2Fview%2F1b0c4494-0899-4bb5-b6c9-66cdbbc23ad0%233ZHX%2F64lX9TrTx5KI1hgMrJzdgbwoRcHTQBxpcYbBQc%3D%22%2C%22refresh%22%5D%7D%5D%7D%5D%7D%2Cnull%2Cnull%2Ctrue%5D
which after decoding (included #fragment):
["",{"children":["view",{"children":[["id","1b0c4494-0899-4bb5-b6c9-66cdbbc23ad0","d"],{"children":["__PAGE__",{},"/view/1b0c4494-0899-4bb5-b6c9-66cdbbc23ad0#3ZHX/64lX9TrTx5KI1hgMrJzdgbwoRcHTQBxpcYbBQc=","refresh"]}]}]},null,null,true]
The secret after # is meant to remain in the browser only. But with Next-Router-State-Tree, that fragment ends up in the request header.
This means:
CDNs log it
Backends may store it
Debugging tools might capture it
In short, sensitive data can leak into places it was never meant to go.
The Bigger Issue: No Official Fix
Here is the tricky part: Next.js does not currently offer any way to disable the Next-Router-State-Tree header. It is baked into the framework.
The only option is to patch the package yourself, either by modifying Next.js internals or using a custom fork. For production apps that rely on fragments or handle sensitive routing, this is a serious limitation.
As part of addressing this in dele.to, we published a workaround patch in PR #7. This removes the header in self-hosted deployments until a proper framework-level fix exists.
Issue has been opened in Next.js for actual fix
Mitigations Until Then
Do not rely on fragments for sensitive data such as tokens or IDs
Strip the header at your proxy or load balancer level (for example, drop it in Nginx or Cloudflare)
Audit your logs to make sure fragments are not being stored
Apply patches like PR #7 to remove the header in self-hosted setups
Push for framework changes because this header should never serialize fragments in the first place
Closing Thoughts
This issue was discovered in the course of building dele.to, but it is relevant to anyone using Next.js in production. It is a reminder that “internal” headers are not always internal once they leave the browser.
Frameworks that ship extra state in headers risk leaking information developers never intended to expose. Until Next.js provides a safe toggle, the only reliable option is to patch the package or strip the header manually.
If you are using Next.js, take a moment to inspect your traffic. You might be surprised what is riding along with your requests.
Thanks to accountless and security researcher Cassie Heart @ Quilibrium.com for finding this issue. Original discussion happened on Farcaster.





