All research

Nuxt: route rules skipped when the path changes case

Nuxt's page router matches paths case-insensitively by default, but its server-side route rules matched case-sensitively. Changing the case of a single letter in the URL rendered the page with its redirect, security headers, proxy, and SSR rules all skipped.

Package
nuxt, @nuxt/nitro-server (npm)
Affected
nuxt 3.0.0 to < 4.6.2, @nuxt/nitro-server < 4.6.2
Fixed in
4.6.2 (Nuxt 3 is end of life, no fix)
Weakness
CWE-178 Case sensitivity, CWE-863 Incorrect authorization
Severity
Moderate
Advisory
GHSA-vjh5-4x84-xrqp

What was the issue

Nuxt is a full-stack framework built on Vue. With the default router.options.sensitive: false, its page router resolves routes case-insensitively, so /Admin renders the same page as /admin. On the server, though, route rules were resolved by Nitro's matcher, which compares keys case-sensitively. A request whose path differed from a rule key only in letter case got the page with none of that rule applied.

export default defineNuxtConfig({
  routeRules: {
    '/admin/**': { redirect: '/login' },
  },
})

With this configuration, /admin/secret redirects to /login, but /Admin/secret rendered app/pages/admin/secret.vue.

Two earlier advisories (GHSA-mm7m-92g8-7m47 and GHSA-hxvh-4h3w-prp9) had case-folded the rules the Nuxt app itself reads. This one covers the rules applied on the server.

Impact

  • redirect: a rule meant to send visitors away from a page was skipped, and the page and its server-fetched data were rendered instead.
  • headers: per-route response headers, including Content-Security-Policy and X-Frame-Options, were not sent.
  • proxy: the request was not proxied.
  • ssr: false and noScripts: the page was server-rendered, or shipped scripts, despite the rule.
  • getRouteRules(event) and matchRouteRules returned no rules for the case variant.

Apps with router.options.sensitive: true in nuxt.config were not affected. Access checks in server middleware, route middleware attached with definePageMeta, or a page's data fetching were not affected either. Only protections that depended on a server-side route rule matching the request path were bypassed.

The fix

Nuxt now resolves server-side route rules itself, decoding and case-folding both the rule keys and the request path unless sensitive is set. The result feeds Nitro's redirect, headers, and proxy handling, the renderer, and the getRouteRules and matchRouteRules helpers.

Response caching (cache and swr) is still matched case-sensitively by Nitro's router, so a case variant gets its rules applied but is not cached. Rules a deployment preset compiles into platform config, such as Vercel's config.json or Netlify's _redirects, are matched by the platform.

What to do

Upgrade to [email protected] or later. Nuxt 3 reached end of life on 31 July 2026 and won't get a fix. If you can't upgrade, set router: { options: { sensitive: true } } in nuxt.config, enforce access control in middleware rather than with a redirect rule, and send security headers on every response rather than per route.

Disclosure timeline

  1. Reported privately to the Nuxt maintainers. Also credited: Aeg1sx and jkrshnmenon.

  2. Advisory published with the fix in nuxt 4.6.2.

The takeaway. When two layers decide whether a path matches, they have to agree on what a path is. Normalize case and encoding once, and make every layer use the same rules.

Coverage and references

More research.

All findings

CriticalGovernmentJul 2026

UMANG Portal

Weaknesses across UMANG services exposed EPFO account numbers, LPG bookings, and Aadhaar numbers stored in plain text.

Read the finding

High · 8.8Open sourceFeb 2026

Rollup bundler

Rollup's filename sanitizer kept ../ sequences, so crafted names could write files outside the output folder.

Read the finding