NUXT · GHSA-f3x9-2chm-73c8 · Package
FIX → 4.6.4
Nuxt matches route rules against the raw request path, so a %2F in the path is not treated as a segment separator while the page router decodes it. An unauthenticated attacker can reach a catch-all page without the auth, redirect or header rules configured for that path.
first public Nov 2, 2026 · 1 source
Could this affect your app?
Free first check from your website. Connect your repo for the full answer.
Applications built with Nuxt 4 that use routeRules to protect paths served by a catch-all page route or a custom route whose param can span several segments. Static routes and API handlers in server/api and server/routes are not affected according to the advisory. Teams on Nuxt 3 have no upstream fix and need a workaround.
Not affected if api-only, no-route-rules, static-export
Who cares netlify, nuxt, self-hosted, vercel
npm ls nuxt and confirm it is below 4.6.4Nuxt matches route rules against the request path with encoded slashes (%2F) left encoded, while the page router decodes them into route params. A request that spells a slash as %2F can therefore render a page through a catch-all route without the route rules configured for the path it resolves to. The fix for GHSA-6gq8-jgmh-3ffp deliberately left reserved escapes such as %2F encoded, so it did not cover this form.
An unauthenticated attacker can bypass route rules that protect paths served by a catch-all page route, or by a custom route whose param can span several segments.
export default defineNuxtConfig({
routeRules: {
'/docs/internal/**': { appMiddleware: 'auth', headers: { 'X-Frame-Options': 'DENY' } },
'/admin/**': { redirect: '/login' },
},
})
With app/pages/docs/[...slug].vue, runs , but renders the same page without running and without the header. With a root , is rendered instead of redirected.
Check it with your coding agent
It reads this page and tells you if your code is affected.
Are you affected?
Ask from here. InfraStack checks the affected versions against your stack and answers in your inbox.
InfraStack checks the affected versions against your stack and answers in Slack, with the evidence and the fix.
routeRules in nuxt.config.ts that protect paths served by a catch-all page such as app/pages/[...slug].vuenuxt 3.x is still in use, since the advisory says Nuxt 3 will not receive a fixUpgrade to 4.6.4
nuxt 4.6.4 for the 4.x line@nuxt/nitro-server 4.6.4 and @nuxt/vite-server 4.6.4/docs/internal/guideauth/docs/internal%2Fguideauthapp/pages/[...slug].vue/admin%2FsecretThe rules skipped include server-side redirect, headers, proxy, ssr and noScripts, and appMiddleware, appLayout and redirect as matched in the app during server rendering and client navigation. getRouteRules returns the same incomplete result.
Whether the page then shows protected content depends on how it reads the route:
definePageMeta({ path: '/docs/:path(.*)' }), route.params.path is internal/guide for both requests.[...slug].vue), route.params.slug is ['internal/guide'] instead of ['internal', 'guide'], so a page that joins the segments (slug.join('/')) to load content loads the protected resource.route.path keeps the encoding (/docs/internal%2Fguide), so a page that loads content by route.path does not resolve it.Not affected:
/docs%2Finternal does not match app/pages/docs/internal.vue.server/api and server/routes. As far as we can tell, Nitro matches these against the same path as its route rules, so a handler and the rules for it agree.Browsers send %2F in a path as written, so a plain link is enough to reach the page.
Fixed in nuxt@4.6.4, together with @nuxt/nitro-server@4.6.4 and @nuxt/vite-server@4.6.4, which it depends on. Route rules are now resolved for both the request path and the path with each %2F read as a separator, and the rules matched by either apply. Nuxt 3 has reached end of life and will not receive a fix.
Reject page requests whose path contains an encoded slash in a server middleware:
// server/middleware/encoded-slash.ts
export default defineEventHandler((event) => {
if (/%2f/i.test(event.path.split('?')[0])) {
throw createError({ statusCode: 404 })
}
})
This also rejects legitimate uses, such as an API route that takes a scoped package name (@scope%2Fname) as a param; narrow the check to the path prefixes your route rules protect if you need those.
A reverse proxy or CDN that rejects or decodes encoded slashes before forwarding the request also removes the mismatch.
Thanks to Riahi Foudhil for reporting this issue.