VYPR
High severityNVD Advisory· Published Jun 12, 2026· Updated Jun 12, 2026

CVE-2026-53721

CVE-2026-53721

Description

A case-sensitivity mismatch between vue-router and Nuxt's routeRules matcher allows attackers to bypass appMiddleware-based authorization gates by altering URL casing.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

A case-sensitivity mismatch between vue-router and Nuxt's routeRules matcher allows attackers to bypass appMiddleware-based authorization gates by altering URL casing.

Vulnerability

In Nuxt versions 3.11.0 through 3.21.6 and 4.0.0 through 4.4.6, the routeRules matcher (built on rou3) performs case-sensitive matching on the URL path, while vue-router defaults to case-insensitive matching (sensitive: false). This causes a mismatch: vue-router will match a page record for a path like /Admin/dashboard, but the getRouteRules() lookup for the same path returns no matching rules. Any appMiddleware declared via routeRules is consequently omitted from the middleware set and never executed, for both server-side rendering (SSR) and client-side navigations. The same issue affects other path-keyed route rules (ssr, redirect, appLayout, and prerender/payload hints). [1]

Exploitation

An attacker who knows a protected URL (e.g., /admin/dashboard) can simply change the case of any static segment in the URL (e.g., /Admin/dashboard or /ADMIN/dashboard) to bypass the appMiddleware authorization gate defined in routeRules. No authentication or special privileges are required, as the attacker can directly request the altered path from a browser or script. The exploit works on both SSR and client navigations. [1]

Impact

Successful exploitation completely skips the appMiddleware authorization logic, rendering the protected page (including any server-fetched data via useFetch or useAsyncData) to the attacker. This is a high-severity authorization bypass (CWE-863) resulting from improper handling of case sensitivity (CWE-178). The attacker gains unauthorized access to pages and data that should be gated by the middleware. [1]

Mitigation

Nuxt has released patches in versions 3.21.7 and 4.4.7 (both fixed on 2026-06-12). The fix normalizes the path to lowercase before passing it to the routeRules matcher, aligning case sensitivity with vue-router's defaults [2][3].

Workarounds for unpatched installations include: - Not relying solely on routeRules.appMiddleware for authorization; idiomatic middleware via definePageMeta({ middleware }) is unaffected [1]. - Setting router.options.sensitive = true in the Nuxt configuration to make vue-router case-sensitive, which eliminates the mismatch but may break existing case-insensitive routes [1]. - Enforcing authorization at the API or data layer as a defense-in-depth measure [1].

AI Insight generated on Jun 12, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected products

2
  • Nuxt/Nuxtreferences2 versions
    (expand)+ 1 more
    • (no CPE)
    • (no CPE)range: >=3.11.0 <3.21.7 OR >=4.0.0 <4.4.7

Patches

2
07e39cd6f26e

fix(nuxt): match route rules case-insensitively to mirror `vue-router`

https://github.com/nuxt/nuxtDaniel RoeMay 28, 2026via nvd-ref
3 files changed · +11 2
  • packages/nitro-server/src/index.ts+1 1 modified
    @@ -432,7 +432,7 @@ export async function bundle (nuxt: Nuxt & { _nitro?: Nitro }): Promise<void> {
           return cachedMatchers[key] = `
           import { defu } from 'defu'
           const matcher = ${matcher}
    -      export default (path) => defu({}, ...matcher('', path).map(r => r.data).reverse())
    +      export default (path) => defu({}, ...matcher('', typeof path === 'string' ? path.toLowerCase() : path).map(r => r.data).reverse())
           `
         },
       })
    
  • packages/nuxt/src/app/composables/manifest.ts+1 1 modified
    @@ -68,7 +68,7 @@ export function getRouteRules (url: string): Record<string, any>
     export function getRouteRules (arg: string | H3Event | { path: string }) {
       const path = typeof arg === 'string' ? arg : arg.path
       try {
    -    return routeRulesMatcher(path)
    +    return routeRulesMatcher(path.toLowerCase())
       } catch (e) {
         console.error('[nuxt] Error matching route rules.', e)
         return {}
    
  • test/nuxt/composables.test.ts+9 0 modified
    @@ -536,6 +536,15 @@ describe.skipIf(!isTestingAppManifest)('app manifests', () => {
           }
         `)
       })
    +  it('matches case-insensitively to mirror vue-router defaults', () => {
    +    expect(getRouteRules({ path: '/Pre/spa/thing' })).toMatchObject({
    +      prerender: true,
    +      ssr: false,
    +    })
    +    expect(getRouteRules({ path: '/PRE/test' })).toMatchObject({
    +      redirect: '/',
    +    })
    +  })
     })
     
     describe('compiled route rules', () => {
    
3f3e3fa7b5ee

fix(nuxt): match route rules case-insensitively to mirror `vue-router`

https://github.com/nuxt/nuxtDaniel RoeMay 28, 2026via nvd-ref
3 files changed · +11 2
  • packages/nitro-server/src/index.ts+1 1 modified
    @@ -419,7 +419,7 @@ export async function bundle (nuxt: Nuxt & { _nitro?: Nitro }): Promise<void> {
           return cachedMatchers[key] = `
           import { defu } from 'defu'
           const matcher = ${matcher}
    -      export default (path) => defu({}, ...matcher('', path).map(r => r.data).reverse())
    +      export default (path) => defu({}, ...matcher('', typeof path === 'string' ? path.toLowerCase() : path).map(r => r.data).reverse())
           `
         },
       })
    
  • packages/nuxt/src/app/composables/manifest.ts+1 1 modified
    @@ -71,7 +71,7 @@ export function getRouteRules (url: string): Record<string, any>
     export function getRouteRules (arg: string | H3Event | { path: string }) {
       const path = typeof arg === 'string' ? arg : arg.path
       try {
    -    return routeRulesMatcher(path)
    +    return routeRulesMatcher(path.toLowerCase())
       } catch (e) {
         console.error('[nuxt] Error matching route rules.', e)
         return {}
    
  • test/nuxt/composables.test.ts+9 0 modified
    @@ -437,6 +437,15 @@ describe.skipIf(!isTestingAppManifest)('app manifests', () => {
           }
         `)
       })
    +  it('matches case-insensitively to mirror vue-router defaults', () => {
    +    expect(getRouteRules({ path: '/Pre/spa/thing' })).toMatchObject({
    +      prerender: true,
    +      ssr: false,
    +    })
    +    expect(getRouteRules({ path: '/PRE/test' })).toMatchObject({
    +      redirect: '/',
    +    })
    +  })
     })
     
     describe('compiled route rules', () => {
    

Vulnerability mechanics

Root cause

"Case-sensitivity mismatch between vue-router (which treats paths case-insensitively by default) and the routeRules matcher (which performed a case-sensitive comparison), allowing attackers to bypass route-rule middleware by using a different case in the URL path."

Attack vector

An attacker sends an HTTP request to the Nuxt application using a URL path that differs in case from the defined route rule (e.g., requesting `/PRE/test` instead of `/pre/test`). Because vue-router matches paths case-insensitively, the request reaches the intended Vue component, but the routeRules matcher fails to apply the associated middleware (such as redirect, prerender, or SSR rules) because it performed a case-sensitive comparison [ref_id=1][ref_id=2]. This allows the attacker to bypass security-critical route rules that would otherwise enforce authentication, redirects, or other protections.

Affected code

The vulnerability exists in two locations: `packages/nuxt/src/app/composables/manifest.ts` in the `getRouteRules` function, and `packages/nitro-server/src/index.ts` in the generated route-rule matcher export [patch_id=5722706][patch_id=5722707]. Both locations passed the raw path to the matcher without case normalization.

What the fix does

The patch normalizes the path to lowercase before passing it to the routeRules matcher. In `packages/nuxt/src/app/composables/manifest.ts`, the `getRouteRules` function now calls `path.toLowerCase()` before invoking `routeRulesMatcher` [patch_id=5722706][patch_id=5722707]. In `packages/nitro-server/src/index.ts`, the generated matcher function also lowercases the path when it is a string [patch_id=5722706][patch_id=5722707]. This ensures the route-rule matching behavior mirrors vue-router's default case-insensitive path matching, closing the bypass.

Preconditions

  • configThe Nuxt application must define route rules (e.g., redirect, prerender, ssr) that are case-sensitive in their path definitions.
  • networkThe attacker must be able to send HTTP requests to the Nuxt application with arbitrary URL paths.
  • configvue-router must be configured with its default case-insensitive path matching behavior.

Generated on Jun 12, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

3

News mentions

0

No linked articles in our index yet.