Cross-request state pollution is when server-side rendering reuses a shared object across requests, so data from one user leaks into another user's page. In Vue SSR it happens when a module-level store is mutated with user-specific data. The fix is a new app and store per request.
Why SSR changes the rules
In a browser-only app, every page visit loads the JavaScript modules fresh, so module-level state belongs to one visitor. In server rendering, the Vue guide explains, application modules are typically initialized once when the server boots. The same module instances serve many requests. A singleton store at module scope is therefore shared by every visitor.
If a request writes user-specific data into that shared object, say a name, an email address or a token loaded for personalization, the next request can render it. Vue calls this cross-request state pollution. It is a data exposure bug that tests with one user will never show.
A picture of the failure
Request A signs in as Ana. The server loads her profile into the shared store and renders her page. Request B arrives from a different visitor before anything resets the store. If B's render reads from the store before its own data loads, it can output Ana's details. Timing makes it intermittent, which makes it hard to notice and easy to dismiss.
The recommended fix
The guide's answer is to create a new instance of the entire application, including the router and global stores, on each request. Then provide the store at the app level and inject it in components, instead of importing a singleton directly. In code that looks like a factory function called per request.
import { createSSRApp } from 'vue'
import { createStore } from './store.js'
export function createApp() {
const app = createSSRApp(/* ... */)
const store = createStore(/* ... */)
app.provide('store', store)
return { app, store }
}
Reinitializing every module per request would also work in principle, but it costs performance, so the factory pattern is preferred. The guide notes that state management libraries like Pinia are designed with this in mind.
How to check your own app
- Search for state declared at module scope: top-level reactive objects, refs and store instances.
- Ask whether any server code path writes request-specific values into them.
- Look for other module-level caches, such as memoized API clients that keep an authorization header.
- Test with two sessions sent in quick succession and compare the HTML each receives.
- Log a request ID with any shared state write, and alert if one request reads a value written by another.
Related SSR notes
Vue's SSR guidance also warns that lifecycle hooks such as mounted do not run on the server, so timers set up in setup code stay alive forever. That is a memory leak rather than a leak of data, but it comes from the same root: server code lives much longer than a browser page.
Quick check
Does this only affect Vue?
The Vue guide describes it for Vue, but any server rendered framework with shared module state needs the same care.
Is a per-request store slow?
Creating a small app and store object per request is the pattern the guide recommends. Re-initializing all modules is the expensive approach it avoids.
Safeguard's free scan at gold.safeguard.sh/scan checks the dependency side of an SSR project, including the rendering and store packages.
Sources
Last reviewed: 19 October 2026.