{"id":"GHSA-2fxj-c3j7-rr69","kind":"vulnerability","aliases":["GHSA-2fxj-c3j7-rr69"],"related":[],"title":"Server island endpoint does not apply `redirect` route rules to `.server.vue` pages","description":"### Summary\r\n\r\nA `.server.vue` page is also rendered as a server island at `/__nuxt_island/page_<name>_<hash>.json`. Nitro applies `routeRules` to the request path, which for an island request is the `/__nuxt_island/...` path rather than the page path, so a `redirect` route rule configured for the page is not applied to its island. A client can request the page island directly and receive the server-rendered HTML of a page whose direct route redirects.\r\n\r\nThe island hash is derived from the component name, props and context. It is not a secret, so no credentials are needed to construct a valid request.\r\n\r\n### Details\r\n\r\nWith the following configuration and a page at `app/pages/secret.server.vue`:\r\n\r\n```ts\r\nexport default defineNuxtConfig({\r\n  routeRules: {\r\n    '/secret': { redirect: '/login' },\r\n  },\r\n})\r\n```\r\n\r\n`GET /secret` responds with `307` and `Location: /login`, but `GET /__nuxt_island/page_secret_<hash>.json?url=%2Fsecret` responds with `200` and the rendered page.\r\n\r\nDuring client-side navigation, the global `manifest-route-rule` middleware applies `redirect` rules before a page island is fetched. When rendering a page island on the server, that middleware did not run, so nothing applied the page's `redirect` rule.\r\n\r\n### Impact\r\n\r\nAn application is affected only if all of the following are true:\r\n\r\n- it has a `.server.vue` page\r\n- a `redirect` route rule matches that page\r\n- it relies on that redirect to keep the page's server-rendered content from being served\r\n\r\nA `redirect` route rule applies to every request, so it is not an authentication or authorisation mechanism. Pages that perform their own access checks (for example in route middleware, which runs for page islands since GHSA-hg3f-28rg-4jxj, or in the server endpoints they fetch from) are not affected. Pages that are not `.server.vue` are not affected either, as their templates are part of the client bundle.\r\n\r\n### Patches\r\n\r\nFixed in `nuxt@4.6.3`. When rendering a page island, Nuxt now applies the page's `redirect` route rule and responds with a redirect instead of rendering the page.\r\n\r\nNuxt 3 has reached end of life and will not receive a fix. Upgrade to Nuxt 4.\r\n\r\n### Workarounds\r\n\r\nProtect sensitive server pages with route middleware or server-side checks rather than a `redirect` route rule, or remove pages that should no longer be served.\r\n\r\nAlternatively, reject island requests for the affected page with a server middleware:\r\n\r\n```ts\r\n// server/middleware/block-secret-island.ts\r\nexport default defineEventHandler((event) => {\r\n  if (event.path.startsWith('/__nuxt_island/page_secret_')) {\r\n    throw createError({ statusCode: 404 })\r\n  }\r\n})\r\n```","summary":"A `.server.vue` page is also reachable as a server island endpoint, and the `redirect` route rule configured for its direct URL is not applied to that endpoint. An attacker can request the island URL and receive the server-rendered HTML of a page that the direct route redirects away.","severity":"low","cvss":3.7,"epss":null,"exploited":false,"exploited_at":null,"products":[{"name":"nuxt","note":">= 3.11.0 < 4.6.3","fixed":["4.6.3"],"affected":"","ecosystem":"npm"}],"ecosystems":["npm"],"tags":["cwe-284","cwe-424","nuxt","package","self-hosted","ssr"],"not_affected_if":["auth-required","no-server-vue-pages","route-middleware-checks"],"how_to_check":"- Look for `.server.vue` files under `app/pages/` or `pages/`.\n- Check `routeRules` in `nuxt.config.ts` for any `redirect` rule that matches one of those pages.\n- Check the installed version with `npm ls nuxt`.","fix":"- `nuxt` `4.6.3` (Nuxt 4 line); Nuxt 3 receives no fix, so upgrade to Nuxt 4.","status":"open","published_at":"2026-10-27T01:10:09+00:00","first_seen_at":"2026-11-09T22:53:16+00:00","triaged_at":"2026-11-09T22:57:21+00:00","updated_at":"2026-11-09T22:57:21+00:00","category":"package","confidence":0.8,"triage":{"at":"2026-11-09T22:57:21Z","model":"claude-haiku-5-5","output":{"fix":"- `nuxt` `4.6.3` (Nuxt 4 line); Nuxt 3 receives no fix, so upgrade to Nuxt 4.","reach":"internet","title":"Nuxt: server island endpoint skips redirect route rules on server pages","impact":"low","summary":"A `.server.vue` page is also reachable as a server island endpoint, and the `redirect` route rule configured for its direct URL is not applied to that endpoint. An attacker can request the island URL and receive the server-rendered HTML of a page that the direct route redirects away.","audience":"developers","category":"package","nicknames":[],"who_cares":["nuxt","self-hosted","ssr"],"confidence":0.8,"fix_status":"available","needs_auth":"no","worst_case":"server-rendered content of a redirected page is readable by anyone","analyst_note":"","how_to_check":"- Look for `.server.vue` files under `app/pages/` or `pages/`.\n- Check `routeRules` in `nuxt.config.ts` for any `redirect` rule that matches one of those pages.\n- Check the installed version with `npm ls nuxt`.","plain_action":"","product_name":"","needs_analyst":false,"not_affected_if":["no-server-vue-pages","auth-required","route-middleware-checks"],"who_is_affected":"Applications that have a `.server.vue` page and a `redirect` route rule matching that page, and that rely on the redirect to keep the page content private. Pages that do their own access checks in route middleware or in the server endpoints they call are not affected, and neither are pages that are not `.server.vue`. A `redirect` rule is not an access control, so teams that never relied on it for protection have less exposure.","user_interaction":"no","product_ecosystem":"","needs_special_setup":"yes","severity_if_unknown":"low"},"cost_usd":0.000849,"input_tokens":5420,"output_tokens":613,"severity_before":"low"},"analyst_status":null,"analyst_note":null,"search":"'-284':25B '-424':27B '/__nuxt_island':112C '/__nuxt_island/page_':96C '/__nuxt_island/page_secret_':211C,469C '/login':200C,208C '/secret':198C,202C '200':217C '28rg':344C '2fsecret':214C '2fxj':15A,20A '3':405C '307':205C '4':420C '4.6.3':379C '404':473C '4jxj':345C 'access':328C 'affect':268C,356C,364C,455C 'also':38B,89C 'altern':449C 'app/pages/secret.server.vue':192C 'appli':6A,57B,99C,130C,234C,258C,310C,387C 'applic':266C 'attack':62B 'authent':319C 'authoris':321C 'away':83B 'bundl':374C 'c3j7':16A,21A 'check':329C,433C 'client':135C,224C,373C 'client-sid':223C 'compon':163C 'configur':50B,124C,187C 'construct':179C 'content':302C 'context':167C 'createerror':471C 'credenti':175C 'cwe':24B,26B 'default':195C,464C 'defineeventhandl':465C 'definenuxtconfig':196C 'deriv':160C 'detail':183C 'direct':53B,80B,141C,153C 'either':365C 'end':408C 'endpoint':3A,44B,60B,350C 'event':466C 'event.path.startswith':468C 'everi':312C 'exampl':331C 'export':194C,463C 'fetch':242C,352C 'fix':376C,416C 'follow':186C,274C 'get':201C,210C 'ghsa':14A,19A,342C 'ghsa-2fxj-c3j7-rr69':13A,18A 'ghsa-hg3f-28rg-4jxj':341C 'global':228C 'hash':158C 'hg3f':343C 'host':32B 'html':74B,148C 'impact':264C 'instead':399C 'island':2A,43B,66B,94C,108C,133C,140C,157C,240C,247C,339C,384C,451C 'json':97C,212C 'keep':295C 'life':410C 'locat':207C 'longer':446C 'manifest':230C 'manifest-route-rul':229C 'match':286C 'mechan':322C 'middlewar':233C,252C,334C,428C,460C 'name':164C 'navig':226C 'need':177C 'nitro':98C 'noth':257C 'nuxt':23A,28B,378C,385C,404C,419C 'packag':29B 'page':12A,36B,77B,87C,117C,127C,139C,151C,190C,221C,239C,246C,260C,281C,288C,297C,323C,338C,357C,383C,389C,403C,425C,442C,456C 'part':370C 'patch':375C 'path':104C,113C,118C 'perform':325C 'prop':165C 'protect':422C 'rather':114C,434C 'reach':407C 'reachabl':39B 'receiv':69B,143C,414C 'redirect':7A,47B,82B,121C,155C,199C,235C,262C,283C,293C,307C,391C,398C,437C 'reject':450C 'reli':290C 'remov':441C 'render':73B,90C,147C,220C,244C,301C,381C,401C 'request':64B,103C,109C,137C,182C,313C,452C 'respond':203C,215C,395C 'rout':8A,48B,81B,122C,154C,231C,284C,308C,333C,392C,427C,438C 'routerul':100C,197C 'rr69':17A,22A 'rule':9A,49B,123C,232C,236C,263C,285C,309C,393C,439C 'run':255C,336C 'secret':172C 'self':31B 'self-host':30B 'sensit':423C 'serv':305C,448C 'server':1A,42B,72B,93C,146C,250C,300C,349C,424C,431C,459C 'server-rend':71B,145C,299C 'server-sid':430C 'server.vue':11A,35B,86C,280C,361C 'server/middleware/block-secret-island.ts':462C 'side':225C,432C 'sinc':340C 'ssr':33B 'statuscod':472C 'summari':84C 'templat':368C 'throw':470C 'true':276C 'ts':193C,461C 'upgrad':417C 'url':54B,67B,213C 'valid':181C 'whose':152C 'workaround':421C","nicknames":[],"mitigations":[],"who_affected":"Applications that have a `.server.vue` page and a `redirect` route rule matching that page, and that rely on the redirect to keep the page content private. Pages that do their own access checks in route middleware or in the server endpoints they call are not affected, and neither are pages that are not `.server.vue`. A `redirect` rule is not an access control, so teams that never relied on it for protection have less exposure.","tier":"C","verified":{},"analyst_revisit_at":null,"fix_status":"available","audience":"developers","plain_action":null,"pkeys":["nuxt"],"ptokens":["nuxt"],"labels":["cwe284","cwe424","low","npm","nuxt","package","selfhosted","ssr"],"campaign_type":null,"search_a":"'2fxj':15A,20A 'appli':6A 'c3j7':16A,21A 'endpoint':3A 'ghsa':14A,19A 'ghsa-2fxj-c3j7-rr69':13A,18A 'island':2A 'nuxt':23A 'page':12A 'redirect':7A 'rout':8A 'rr69':17A,22A 'rule':9A 'server':1A 'server.vue':11A","search_b":"'-284':25B '-424':27B 'also':38B 'appli':57B 'attack':62B 'away':83B 'configur':50B 'cwe':24B,26B 'direct':53B,80B 'endpoint':44B,60B 'host':32B 'html':74B 'island':43B,66B 'nuxt':28B 'packag':29B 'page':36B,77B 'reachabl':39B 'receiv':69B 'redirect':47B,82B 'render':73B 'request':64B 'rout':48B,81B 'rule':49B 'self':31B 'self-host':30B 'server':42B,72B 'server-rend':71B 'server.vue':35B 'ssr':33B 'url':54B,67B","search_c":"'/__nuxt_island':112C '/__nuxt_island/page_':96C '/__nuxt_island/page_secret_':211C,469C '/login':200C,208C '/secret':198C,202C '200':217C '28rg':344C '2fsecret':214C '3':405C '307':205C '4':420C '4.6.3':379C '404':473C '4jxj':345C 'access':328C 'affect':268C,356C,364C,455C 'also':89C 'altern':449C 'app/pages/secret.server.vue':192C 'appli':99C,130C,234C,258C,310C,387C 'applic':266C 'authent':319C 'authoris':321C 'bundl':374C 'check':329C,433C 'client':135C,224C,373C 'client-sid':223C 'compon':163C 'configur':124C,187C 'construct':179C 'content':302C 'context':167C 'createerror':471C 'credenti':175C 'default':195C,464C 'defineeventhandl':465C 'definenuxtconfig':196C 'deriv':160C 'detail':183C 'direct':141C,153C 'either':365C 'end':408C 'endpoint':350C 'event':466C 'event.path.startswith':468C 'everi':312C 'exampl':331C 'export':194C,463C 'fetch':242C,352C 'fix':376C,416C 'follow':186C,274C 'get':201C,210C 'ghsa':342C 'ghsa-hg3f-28rg-4jxj':341C 'global':228C 'hash':158C 'hg3f':343C 'html':148C 'impact':264C 'instead':399C 'island':94C,108C,133C,140C,157C,240C,247C,339C,384C,451C 'json':97C,212C 'keep':295C 'life':410C 'locat':207C 'longer':446C 'manifest':230C 'manifest-route-rul':229C 'match':286C 'mechan':322C 'middlewar':233C,252C,334C,428C,460C 'name':164C 'navig':226C 'need':177C 'nitro':98C 'noth':257C 'nuxt':378C,385C,404C,419C 'page':87C,117C,127C,139C,151C,190C,221C,239C,246C,260C,281C,288C,297C,323C,338C,357C,383C,389C,403C,425C,442C,456C 'part':370C 'patch':375C 'path':104C,113C,118C 'perform':325C 'prop':165C 'protect':422C 'rather':114C,434C 'reach':407C 'receiv':143C,414C 'redirect':121C,155C,199C,235C,262C,283C,293C,307C,391C,398C,437C 'reject':450C 'reli':290C 'remov':441C 'render':90C,147C,220C,244C,301C,381C,401C 'request':103C,109C,137C,182C,313C,452C 'respond':203C,215C,395C 'rout':122C,154C,231C,284C,308C,333C,392C,427C,438C 'routerul':100C,197C 'rule':123C,232C,236C,263C,285C,309C,393C,439C 'run':255C,336C 'secret':172C 'sensit':423C 'serv':305C,448C 'server':93C,146C,250C,300C,349C,424C,431C,459C 'server-rend':145C,299C 'server-sid':430C 'server.vue':86C,280C,361C 'server/middleware/block-secret-island.ts':462C 'side':225C,432C 'sinc':340C 'statuscod':472C 'summari':84C 'templat':368C 'throw':470C 'true':276C 'ts':193C,461C 'upgrad':417C 'url':213C 'valid':181C 'whose':152C 'workaround':421C","cause_entry":null,"cause_path":null,"disclosures_checked_at":null,"first_public_at":"2026-10-27T01:10:09+00:00","kev":null,"cvss_vector":null,"cvss_by":null,"poc_count":0,"sources":[{"source":"ghsa-repo","external_id":"GHSA-2fxj-c3j7-rr69","url":"https://github.com/nuxt/nuxt/security/advisories/GHSA-2fxj-c3j7-rr69","severity":"low","published_at":"2026-10-27T01:10:09+00:00","modified_at":"2026-10-29T13:28:21+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":110444,"url":"https://github.com/nuxt/nuxt/security/advisories/GHSA-2fxj-c3j7-rr69","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"}