{"id":"GHSA-r99w-jh2x-3mg9","kind":"vulnerability","aliases":["GHSA-r99w-jh2x-3mg9"],"related":[],"title":"Incomplete fix for GHSA-9v7c-q64q-vv33: a leading `//` in the request path bypasses `server/middleware` path checks and server route rules on page routes","description":"### Summary\n\nSince `4.6.0`, Nuxt parses a request path that starts with `//` as a URL with a host before rendering a page, so `//x/admin` renders the page at `/admin`. Nitro's routing, Nitro's route rules and `server/middleware` see the path as `//x/admin`, so server-side checks keyed on `/admin` do not apply. The fix for [GHSA-9v7c-q64q-vv33](https://github.com/nuxt/nuxt/security/advisories/GHSA-9v7c-q64q-vv33) in `4.6.3` covered dot segments but not this form.\n\n### Impact\n\nAn unauthenticated attacker can bypass server-side controls that match on the request path of a page route.\n\nA route-scoped server handler is skipped:\n\n```ts\n// nuxt.config.ts\nexport default defineNuxtConfig({\n  serverHandlers: [{ route: '/admin', middleware: true, handler: '~~/server/admin-guard.ts' }],\n})\n```\n\n```ts\n// server/admin-guard.ts\nexport default defineEventHandler((event) => {\n  if (!isAuthenticated(event)) {\n    throw createError({ statusCode: 401 })\n  }\n})\n```\n\nWith this handler, `/admin` returns 401, but `//x/admin` and `///x/admin` render `app/pages/admin.vue` along with the data it fetches during server rendering. `server/middleware` that checks `event.path.startsWith('/admin')` is skipped in the same way.\n\nServer-side `redirect` and `headers` route rules are also skipped, so `'/admin/**': { redirect: '/login' }` does not redirect `//x/admin/secret`, and per-route security headers such as `Content-Security-Policy` are not sent.\n\nBrowsers send a path such as `//x/admin` as written, so a plain link is enough to reach the page.\n\nNot affected:\n\n- API handlers in `server/api` and `server/routes`, which Nitro does not match for these paths.\n- `appMiddleware` route rules and middleware declared with `definePageMeta`, which are matched against the resolved route.\n- Nuxt `3.x` and `4.0.0` to `4.5.x`, which pass the path to the page router unchanged, so these requests 404.\n\n### Patches\n\nFixed in `@nuxt/nitro-server@4.6.4`, which `nuxt@4.6.4` depends on. The renderer now uses the request path exactly as Nitro matched it, so a path starting with `//` no longer resolves to a different page.\n\n### Workarounds\n\nReject request paths that start with `//` in a server middleware:\n\n```ts\n// server/middleware/double-slash.ts\nexport default defineEventHandler((event) => {\n  if (event.path.startsWith('//')) {\n    throw createError({ statusCode: 404 })\n  }\n})\n```\n\nA reverse proxy or CDN that merges repeated slashes before forwarding the request also removes the mismatch.\n\n### Credits\n\nThanks to @kq5y for reporting this issue.","summary":"Nuxt 4.6.0 to 4.6.3 renders the page at /admin for a request path like //x/admin, while Nitro route rules and server middleware see the unmatched path. An unauthenticated attacker can skip server-side checks such as auth guards, redirects and security headers on page routes.","severity":"medium","cvss":6.5,"epss":null,"exploited":false,"exploited_at":null,"products":[{"name":"@nuxt/nitro-server","fixed":["4.6.4"],"affected":">= 4.6.0, < 4.6.4","ecosystem":"npm"}],"ecosystems":["npm"],"tags":["auth-middleware","cwe-436","cwe-863","nitro","nuxt","package","self-hosted"],"not_affected_if":["api-only","route-rules-unused","static-export","version-outside-range"],"how_to_check":"- Run `npm ls @nuxt/nitro-server` in the project and check whether the version is `4.6.0` to `4.6.3`.\n- Look for `serverHandlers` with `middleware: true`, `redirect` or `headers` route rules, or `server/middleware` that check paths like `/admin`.","fix":"- `@nuxt/nitro-server` `4.6.4` (nuxt `4.6.4` depends on it)\n- Workaround from the advisory: reject paths starting with `//` in a server middleware, or strip repeated slashes at a reverse proxy or CDN.","status":"open","published_at":"2026-11-02T21:00:20+00:00","first_seen_at":"2026-11-09T22:53:16+00:00","triaged_at":"2026-11-09T22:54:39+00:00","updated_at":"2026-11-09T22:54:39+00:00","category":"package","confidence":0.8,"triage":{"at":"2026-11-09T22:54:39Z","model":"claude-haiku-5-5","output":{"fix":"- `@nuxt/nitro-server` `4.6.4` (nuxt `4.6.4` depends on it)\n- Workaround from the advisory: reject paths starting with `//` in a server middleware, or strip repeated slashes at a reverse proxy or CDN.","reach":"internet","title":"Nuxt: a leading double slash bypasses server路径 checks on page routes","impact":"medium","summary":"Nuxt 4.6.0 to 4.6.3 renders the page at /admin for a request path like //x/admin, while Nitro route rules and server middleware see the unmatched path. An unauthenticated attacker can skip server-side checks such as auth guards, redirects and security headers on page routes.","audience":"developers","category":"package","nicknames":[],"who_cares":["nuxt","nitro","self-hosted","auth-middleware"],"confidence":0.8,"fix_status":"available","needs_auth":"no","worst_case":"Unauthenticated users reach protected server-rendered pages and their data","analyst_note":"","how_to_check":"- Run `npm ls @nuxt/nitro-server` in the project and check whether the version is `4.6.0` to `4.6.3`.\n- Look for `serverHandlers` with `middleware: true`, `redirect` or `headers` route rules, or `server/middleware` that check paths like `/admin`.","plain_action":"","product_name":"","needs_analyst":false,"not_affected_if":["static-export","api-only","route-rules-unused","version-outside-range"],"who_is_affected":"Apps built on Nuxt 4.6.0 to 4.6.3 that enforce auth, redirects or headers through server middleware, serverHandlers or route rules keyed on page paths. Nuxt 3.x, 4.0.0 to 4.5.x, and API handlers in server/api and server/routes are not affected per the advisory; appMiddleware and definePageMeta middleware are not affected either.","user_interaction":"no","product_ecosystem":"","needs_special_setup":"yes","severity_if_unknown":"unknown"},"cost_usd":0.000905,"input_tokens":5574,"output_tokens":695,"severity_before":"medium"},"analyst_status":null,"analyst_note":null,"search":"'-436':41B '-863':43B '/admin':58B,123C,145C,204C,225C,247C,266C '/login':268C '/nuxt/nuxt/security/advisories/ghsa-9v7c-q64q-vv33)':159C '/server/admin-guard.ts':208C '/x/admin':64B,118C,137C,229C,231C,294C '/x/admin/secret':272C '3':339C '3mg9':30A,35A '4.0.0':342C '4.5':344C '4.6.0':51B,98C '4.6.3':53B,161C '4.6.4':363C,366C '401':221C,227C '404':358C,415C '9v7c':6A,154C 'affect':308C 'along':234C 'also':263C,429C 'api':309C 'app/pages/admin.vue':233C 'appli':148C 'appmiddlewar':323C 'attack':78B,172C 'auth':38B,87B 'auth-middlewar':37B 'browser':288C 'bypass':15A,174C 'cdn':420C 'check':18A,84B,142C,245C 'content':282C 'content-security-polici':281C 'control':178C 'cover':162C 'createerror':219C,413C 'credit':433C 'cwe':40B,42B 'data':237C 'declar':328C 'default':200C,212C,407C 'defineeventhandl':213C,408C 'definenuxtconfig':201C 'definepagemeta':330C 'depend':367C 'differ':391C 'dot':163C 'enough':302C 'event':214C,217C,409C 'event.path.startswith':246C,411C 'exact':376C 'export':199C,211C,406C 'fetch':239C 'fix':2A,150C,360C 'form':168C 'forward':426C 'ghsa':5A,27A,32A,153C 'ghsa-9v7c-q64q-vv33':4A,152C 'ghsa-r99w-jh2x-3mg9':26A,31A 'github.com':158C 'github.com/nuxt/nuxt/security/advisories/ghsa-9v7c-q64q-vv33)':157C 'guard':88B 'handler':194C,207C,224C,310C 'header':92B,259C,278C 'host':49B,112C 'impact':169C 'incomplet':1A 'isauthent':216C 'issu':440C 'jh2x':29A,34A 'key':143C 'kq5y':436C 'lead':10A 'like':63B 'link':300C 'longer':387C 'match':180C,319C,333C,379C 'merg':422C 'middlewar':39B,71B,205C,327C,403C 'mismatch':432C 'nitro':44B,66B,124C,127C,316C,378C 'nuxt':45B,50B,99C,338C,365C 'nuxt.config.ts':198C 'nuxt/nitro-server':36A,362C 'packag':46B 'page':24A,56B,94B,116C,121C,187C,306C,352C,392C 'pars':100C 'pass':347C 'patch':359C 'path':14A,17A,62B,75B,103C,135C,184C,291C,322C,349C,375C,383C,396C 'per':275C 'per-rout':274C 'plain':299C 'polici':284C 'proxi':418C 'q64q':7A,155C 'r99w':28A,33A 'reach':304C 'redirect':89B,257C,267C,271C 'reject':394C 'remov':430C 'render':54B,114C,119C,232C,242C,370C 'repeat':423C 'report':438C 'request':13A,61B,102C,183C,357C,374C,395C,428C 'resolv':336C,388C 'return':226C 'revers':417C 'rout':21A,25A,67B,95B,126C,129C,188C,191C,203C,260C,276C,324C,337C 'route-scop':190C 'router':353C 'rule':22A,68B,130C,261C,325C 'scope':192C 'secur':91B,277C,283C 'see':72B,133C 'segment':164C 'self':48B 'self-host':47B 'send':289C 'sent':287C 'server':20A,70B,82B,140C,176C,193C,241C,255C,402C 'server-sid':81B,139C,175C,254C 'server/admin-guard.ts':210C 'server/api':312C 'server/middleware':16A,132C,243C 'server/middleware/double-slash.ts':405C 'server/routes':314C 'serverhandl':202C 'side':83B,141C,177C,256C 'sinc':97C 'skip':80B,196C,249C,264C 'slash':424C 'start':105C,384C,398C 'statuscod':220C,414C 'summari':96C 'thank':434C 'throw':218C,412C 'true':206C 'ts':197C,209C,404C 'unauthent':77B,171C 'unchang':354C 'unmatch':74B 'url':109C 'use':372C 'vv33':8A,156C 'way':253C 'workaround':393C 'written':296C 'x':340C,345C","nicknames":[],"mitigations":[],"who_affected":"Apps built on Nuxt 4.6.0 to 4.6.3 that enforce auth, redirects or headers through server middleware, serverHandlers or route rules keyed on page paths. Nuxt 3.x, 4.0.0 to 4.5.x, and API handlers in server/api and server/routes are not affected per the advisory; appMiddleware and definePageMeta middleware are not affected either.","tier":"C","verified":{},"analyst_revisit_at":null,"fix_status":"available","audience":"developers","plain_action":null,"pkeys":["nitroserver"],"ptokens":["nitro","nuxt","server"],"labels":["authmiddleware","cwe436","cwe863","medium","nitro","npm","nuxt","package","selfhosted"],"campaign_type":null,"search_a":"'3mg9':30A,35A '9v7c':6A 'bypass':15A 'check':18A 'fix':2A 'ghsa':5A,27A,32A 'ghsa-9v7c-q64q-vv33':4A 'ghsa-r99w-jh2x-3mg9':26A,31A 'incomplet':1A 'jh2x':29A,34A 'lead':10A 'nuxt/nitro-server':36A 'page':24A 'path':14A,17A 'q64q':7A 'r99w':28A,33A 'request':13A 'rout':21A,25A 'rule':22A 'server':20A 'server/middleware':16A 'vv33':8A","search_b":"'-436':41B '-863':43B '/admin':58B '/x/admin':64B '4.6.0':51B '4.6.3':53B 'attack':78B 'auth':38B,87B 'auth-middlewar':37B 'check':84B 'cwe':40B,42B 'guard':88B 'header':92B 'host':49B 'like':63B 'middlewar':39B,71B 'nitro':44B,66B 'nuxt':45B,50B 'packag':46B 'page':56B,94B 'path':62B,75B 'redirect':89B 'render':54B 'request':61B 'rout':67B,95B 'rule':68B 'secur':91B 'see':72B 'self':48B 'self-host':47B 'server':70B,82B 'server-sid':81B 'side':83B 'skip':80B 'unauthent':77B 'unmatch':74B","search_c":"'/admin':123C,145C,204C,225C,247C,266C '/login':268C '/nuxt/nuxt/security/advisories/ghsa-9v7c-q64q-vv33)':159C '/server/admin-guard.ts':208C '/x/admin':118C,137C,229C,231C,294C '/x/admin/secret':272C '3':339C '4.0.0':342C '4.5':344C '4.6.0':98C '4.6.3':161C '4.6.4':363C,366C '401':221C,227C '404':358C,415C '9v7c':154C 'affect':308C 'along':234C 'also':263C,429C 'api':309C 'app/pages/admin.vue':233C 'appli':148C 'appmiddlewar':323C 'attack':172C 'browser':288C 'bypass':174C 'cdn':420C 'check':142C,245C 'content':282C 'content-security-polici':281C 'control':178C 'cover':162C 'createerror':219C,413C 'credit':433C 'data':237C 'declar':328C 'default':200C,212C,407C 'defineeventhandl':213C,408C 'definenuxtconfig':201C 'definepagemeta':330C 'depend':367C 'differ':391C 'dot':163C 'enough':302C 'event':214C,217C,409C 'event.path.startswith':246C,411C 'exact':376C 'export':199C,211C,406C 'fetch':239C 'fix':150C,360C 'form':168C 'forward':426C 'ghsa':153C 'ghsa-9v7c-q64q-vv33':152C 'github.com':158C 'github.com/nuxt/nuxt/security/advisories/ghsa-9v7c-q64q-vv33)':157C 'handler':194C,207C,224C,310C 'header':259C,278C 'host':112C 'impact':169C 'isauthent':216C 'issu':440C 'key':143C 'kq5y':436C 'link':300C 'longer':387C 'match':180C,319C,333C,379C 'merg':422C 'middlewar':205C,327C,403C 'mismatch':432C 'nitro':124C,127C,316C,378C 'nuxt':99C,338C,365C 'nuxt.config.ts':198C 'nuxt/nitro-server':362C 'page':116C,121C,187C,306C,352C,392C 'pars':100C 'pass':347C 'patch':359C 'path':103C,135C,184C,291C,322C,349C,375C,383C,396C 'per':275C 'per-rout':274C 'plain':299C 'polici':284C 'proxi':418C 'q64q':155C 'reach':304C 'redirect':257C,267C,271C 'reject':394C 'remov':430C 'render':114C,119C,232C,242C,370C 'repeat':423C 'report':438C 'request':102C,183C,357C,374C,395C,428C 'resolv':336C,388C 'return':226C 'revers':417C 'rout':126C,129C,188C,191C,203C,260C,276C,324C,337C 'route-scop':190C 'router':353C 'rule':130C,261C,325C 'scope':192C 'secur':277C,283C 'see':133C 'segment':164C 'send':289C 'sent':287C 'server':140C,176C,193C,241C,255C,402C 'server-sid':139C,175C,254C 'server/admin-guard.ts':210C 'server/api':312C 'server/middleware':132C,243C 'server/middleware/double-slash.ts':405C 'server/routes':314C 'serverhandl':202C 'side':141C,177C,256C 'sinc':97C 'skip':196C,249C,264C 'slash':424C 'start':105C,384C,398C 'statuscod':220C,414C 'summari':96C 'thank':434C 'throw':218C,412C 'true':206C 'ts':197C,209C,404C 'unauthent':171C 'unchang':354C 'url':109C 'use':372C 'vv33':156C 'way':253C 'workaround':393C 'written':296C 'x':340C,345C","cause_entry":null,"cause_path":null,"disclosures_checked_at":null,"first_public_at":"2026-11-02T21:00:20+00:00","kev":null,"cvss_vector":null,"cvss_by":null,"poc_count":0,"sources":[{"source":"ghsa-repo","external_id":"GHSA-r99w-jh2x-3mg9","url":"https://github.com/nuxt/nuxt/security/advisories/GHSA-r99w-jh2x-3mg9","severity":"medium","published_at":"2026-11-02T21:00:20+00:00","modified_at":"2026-11-02T21:00:20+00:00","first_seen_at":"2026-11-09T22:53:16+00:00","fetched_at":"2026-11-09T22:53:16+00:00","note":null}],"documents":[{"id":110427,"url":"https://github.com/nuxt/nuxt/security/advisories/GHSA-r99w-jh2x-3mg9","title":"Maintainer's advisory: github.com","kind":"vendor","source":"ghsa-repo","publisher":"vendor","text":null,"published_at":null,"relation":"advisory","confidence":null,"note":"Maintainer's advisory: github.com"}],"disclosures":[],"exploit_sources":[],"notice":"Free to read, with fair use of about 60 pages a minute. For higher limits or API access: sales@infrastack.ai"}