On 10 November 2026 the Vite team published a critical security advisory, GHSA-p4f5-76q4-ffq7: a single crafted HTTP request to an exposed Vite dev server could read files outside the allowed folders and, in the worst case, run code on the machine. Patched versions are already on npm. This guide explains who is affected, how to check your projects in a minute, and how to harden your dev setup so the next dev-server bug hurts less.
Am I affected?
You are affected only if both of these are true:
- You run a vulnerable Vite version (or
vite-plus1.5.0 or earlier), and - Your dev server is exposed to the network, using
--hostor theserver.hostconfig option.
The default vite command binds to localhost, which is not exploitable from other machines. But many Docker, devcontainer and cloud IDE setups pass --host so the browser on the host can reach the container. If that describes your team, treat this as urgent.
Vulnerable and patched versions
| Vulnerable range | Upgrade to |
|---|---|
| 8.3.0 – 8.3.6 | 8.3.7 or later |
| 8.2.0 – 8.2.4 | 8.2.5 |
| 8.0.0 – 8.1.6 | 8.1.7 |
| 7.0.0 – 7.3.7 | 7.3.8 |
| 4.0.0 – 6.4.5 | 6.4.6 |
| vite-plus ≤ 1.5.0 | 1.5.1 |
The latest 8.x release at the time of writing is 8.3.8, published on 11 November, which also includes the fix.
What went wrong (in plain English)
Vite’s resolver has two internal virtual modules, __vite-browser-external:<suffix> and __vite-optional-peer-dep:<suffix>. They embedded a module ID taken from the request URL without escaping it. Because the suffix could be anything, an attacker could inject code into those generated modules with a request to /@id/....
The advisory describes two consequences:
- File read bypass: modules referenced by a trusted module are allowed past the
server.fscheck. Injecting animportof a file outside your project made that file fetchable. - Code execution: Vite uses
evalinternally to extract values fornew Workerandimport.meta.glob. Injected code could be evaluated on the server.
In Vite 8 the vulnerable code lives in the Rolldown binary. Vite 8.3.7 and 8.2.5 rely on the fix in Rolldown 1.2.17, and Vite 8.1.7 relies on Rolldown 1.1.7, so upgrading Vite brings in the right Rolldown automatically.
Step 1: Find which Vite version you really have
Check the installed version, not just the range in package.json:
# npm
npm ls vite
# pnpm (all workspaces)
pnpm why vite -r
# yarn
yarn why vite
# Quick check from Node
node -p "require('vite/package.json').version"
Remember that frameworks bundle Vite as a dependency. SvelteKit, Nuxt, Astro, Remix/React Router, Vitest and Storybook projects can all pull in Vite even if you never installed it yourself.
Step 2: Upgrade
# Vite 8 projects
npm install -D vite@^8.3.7
# Still on Vite 7
npm install -D vite@^7.3.8
# Vite 4–6 (via the 6.x line)
npm install -D vite@^6.4.6
If Vite arrives through a framework and the framework has not bumped its dependency yet, force the patched version with an override:
// package.json (npm)
{
"overrides": {
"vite": "^8.3.7"
}
}
// package.json (pnpm)
{
"pnpm": {
"overrides": {
"vite": "^8.3.7"
}
}
}
Then reinstall, run npm ls vite again and confirm only patched versions remain. Commit the updated lockfile so CI and teammates get the same tree.
Step 3: Audit how you expose the dev server
Search your repos for places that open the dev server to the network:
git grep -nE -e '--host' -e 'host:' -- package.json 'vite.config.*' 'docker-compose*.yml' .devcontainer
For each hit, ask whether it really needs to listen on all interfaces. Safer options:
- Bind to localhost and use port forwarding (VS Code devcontainers and most cloud IDEs forward ports for you).
- In Docker, publish the port only on the loopback interface:
ports: ["127.0.0.1:5173:5173"]instead of"5173:5173". - Never expose a dev server on a public IP, a shared office network or a cloud VM without a firewall.
// vite.config.ts – explicit, safe default
import { defineConfig } from 'vite'
export default defineConfig({
server: {
host: '127.0.0.1',
strictPort: true,
},
})
Step 4: Keep server.fs strict
Vite’s server.fs settings decide which files the dev server may serve. They did not stop this bug on their own, but they limit damage from others. Do not loosen them unless you must:
export default defineConfig({
server: {
fs: {
strict: true, // the default – keep it
deny: ['.env', '.env.*', '*.{crt,pem,key}'],
},
},
})
Was my dev server probed?
Exploitation needs requests to /@id/__vite-browser-external: or /@id/__vite-optional-peer-dep: with unusual content after the colon. If you ran an exposed dev server behind a reverse proxy or in a container with request logging, search those logs:
grep -E "/@id/__vite-(browser-external|optional-peer-dep):" access.log
Legitimate requests to these modules do happen during normal development, so look for entries with long, encoded payloads or requests from unexpected IP addresses. If you find suspicious hits, rotate any secrets that were readable on that machine, such as .env files and cloud credentials.
Step 5: Catch it automatically next time
- Run
npm audit(orpnpm audit) in CI and fail oncritical. - Enable Dependabot or Renovate security updates so patched versions arrive as pull requests.
- Rebuild devcontainer and Docker images after upgrading; cached layers may still hold the old
node_modules.
# Example CI step
npm ci
npm audit --audit-level=critical
A pattern worth noticing
This is not the first Vite dev-server advisory of the autumn. In October, Vite fixed file-exposure bugs involving server.fs.allow, server.fs.deny and ?vite-wasm-instance, and every one of them shared the same precondition: a dev server exposed with --host. The lesson is broader than one CVE: a dev server is not built to face untrusted networks. Keeping it on localhost removes a whole class of risk.
Key takeaway
Upgrade Vite to 8.3.7+ (or 8.2.5, 8.1.7, 7.3.8, 6.4.6 on older lines), check transitive copies with npm ls vite, and stop exposing dev servers with --host unless you truly need to. Read the full GHSA-p4f5-76q4-ffq7 advisory and the Vite changelog for details.
