Skip to content
Putting technology to work.
Insights to guide decisions and action.

Search articles

5 path spellings that slip past Nuxt admin route protection, and the fix in 4.6.4

Table of contents · 7 items

Members-only pages and internal admin screens: sites built with Nuxt sometimes protect them by writing rules like "require authentication if the path starts with /admin" in server/middleware, or "redirect /members/** to the login page" in routeRules.

Three of the Nuxt advisories published in October 2026 are exactly about this: protection based on checking the path can be bypassed just by slightly changing how the path is written. Requesting something like /%2e/admin or /Admin returns the supposedly protected page, rendered on the server. Then on November 2, two more advisories were published covering the //x/admin and %2F spellings, stating that the fixes for two of those issues were incomplete.

This article sets out the affected conditions and fixed versions, and shows the results of running Nuxt locally before and after the fixes. Because some issues will not be fixed for sites still on Nuxt 3, we also show workarounds for when you cannot upgrade, following the original advisories.

The five issues

All five are caused by a mismatch in how the page-rendering side and the server-side protection interpret the path. All are rated Moderate in severity.

AdvisoryBypassing spellingProtection bypassedAffected versionsFixed version
GHSA-9v7c-q64q-vv33/%2e/admin、/x/%2e%2e/adminPath-checking server/middleware, and redirect and headers rules4.6.0〜4.6.24.6.3
GHSA-r99w-jh2x-3mg9 (gap in the 9v7c fix)//x/adminSame as above4.6.0〜4.6.34.6.4
GHSA-vjh5-4x84-xrqp/Admin、/MEMBERS/secretredirect, headers, proxy and ssr rules4.0.0 to 4.6.1, all 3.x versions4.6.2 (none for 3.x)
GHSA-6gq8-jgmh-3ffp/users/%61dmin (dynamic and catch-all routes)appMiddleware and other rules4.0.0〜4.4.3、3.11.0〜3.21.34.4.4・3.21.4
GHSA-f3x9-2chm-73c8 (gap in the 6gq8 fix)/admin%2Fsecret (catch-all routes)redirect, headers, appMiddleware and other rules4.0.0 to 4.6.3, all 3.x versions4.6.4 (none for 3.x)

Dot segments (9v7c): Starting with 4.6.0, Nuxt resolves . and .. in the path (including spellings like %2e) before rendering a page. However, server/middleware and server-side rules look at the path before it is resolved. As a result, /%2e/admin is rendered as the /admin page, but protection that checks /admin does not apply. If you attach Content-Security-Policy or similar per route, those headers are not added either. The advisory notes that because browsers resolve such paths before sending them, exploitation requires a client that sends the path exactly as written. The server/api and server/routes APIs, middleware attached with definePageMeta, and appMiddleware rules are not affected.

Leading // (r99w): The fix in 4.6.3 covered only dot segments. Nuxt 4.6.0 and later reads paths starting with // as URLs with a hostname, so //x/admin is rendered as the /admin page. The server-side check still sees //x/admin, so the same protection as in 9v7c is bypassed. The advisory notes that browsers send this one as written, so it can be reached with an ordinary link.

Upper and lower case (vjh5): By default (router.options.sensitive: false), Nuxt's page router is case-insensitive, so /Admin still shows the /admin page. Server-side rules, however, were matched case-sensitively. Even if you write '/admin/**': { redirect: '/login' }, the page is rendered for /Admin/secret.

Percent-encoding (6gq8): This applies when a dynamic route such as app/pages/users/[id].vue has a rule attached only to a specific path such as '/users/admin': { appMiddleware: 'auth' }. If you request /users/%61dmin (%61 is a), the page is rendered with id as admin, but the /users/admin rule does not match and auth does not run. From 4.4.4 and 3.21.4 onward, a dependency normalizes the path, so this does not occur in the default configuration, but it is said to be possible if you pin the dependency to an older version. The comparison itself was fixed in 4.5.2 and 3.21.11, and the advisory recommends updating to these versions.

Encoded slashes (f3x9): The 6gq8 fix deliberately left %2F encoded. As a result, on catch-all routes such as [...slug].vue, writing the slash as %2F, as in /admin%2Fsecret, bypasses rules such as redirect for /admin/**. Browsers send this one as written too. The advisory notes that whether the protected page's content is exposed depends on how the page reads the path.

Some will not be fixed in Nuxt 3

The roadmap on Nuxt's official site states that "Nuxt 3 reached end of support on July 31, 2026, and will no longer receive bug fixes or security fixes." The latest 3.x version is 3.21.11 (released August 5), and none has been released since. The advisories also state that the case-sensitivity issue (vjh5) and the encoded slash issue (f3x9) will not be fixed in Nuxt 3. Sites still on Nuxt 3 will need to protect themselves with the workarounds described below or consider migrating to Nuxt 4. According to the advisories, the dot segment and leading // issues (9v7c, r99w) do not affect Nuxt 3 or 4.5.x and earlier, because the relevant requests return 404 (the editorial team has not tested this).

What we found in local testing

On November 11, 2026, we built a minimal app and tested it in two rounds. In both, we started a nuxt build build on localhost and sent requests with curl using --path-as-is (which sends the path exactly as written). The first and second rounds used different environments.

Diagram showing, in four boxes by Nuxt version, the results of requests with altered path spellings. Results from two runs on November 11, 2026, using curl on localhost to send paths exactly as written. On 4.6.1, /%2e/admin and /Members/secret returned 200. On 4.6.3, /%2e/admin returned 404 and /Members/secret returned a 307 to /login, but //x/admin returned 200 with the admin screen, and /admin%2Fsecret returned 200 with the catch-all page rendered. On 4.6.4, //x/admin returned 404 and /admin%2Fsecret returned a 307 to /login. On 4.6.3 with the two workarounds double-slash.ts and encoded-slash.ts added, both //x/admin and /admin%2Fsecret returned 404

Round 1: dot segments and uppercase (4.6.1, 4.6.3)

The environment was Linux with Node.js v22.23.4. Using a setup with a /admin page, logic in server/middleware that returns 401 when event.path starts with /admin, and a '/members/**': { redirect: '/login' } rule, we compared six kinds of requests.

  • 4.6.1: /admin returned 401, but /%2e/admin and /x/%2e%2e/admin returned 200 with the admin screen. /members/secret was redirected (307) to /login, but /Members/secret and /MEMBERS/secret were not redirected and returned the page with 200.
  • 4.6.3: Dot segment requests returned 404, and uppercase requests were redirected (307) to /login.
  • 4.6.1 with workarounds: Adding two of the workarounds below, for case sensitivity and dot segments, made both the dot segment requests and /Members/secret return 404.

In round 1, we did not send the //x/admin or %2F spellings.

Round 2: leading // and %2F (4.6.3, 4.6.4)

The environment was macOS (Darwin arm64) with Node.js v22.23.4. We compared the spellings from the two issues added on November 2 using two apps.

  • App A: The same setup as round 1. We added //x/admin, ///x/admin and //x/members/secret to the six kinds of requests from round 1.
  • App B: A setup with a catch-all page (app/pages/[...slug].vue) at the root and redirect: '/login' rules on '/admin/**' and '/members/**'. We requested /admin%2Fsecret, /admin%2fsecret, /members%2Fsecret, //x/admin/secret and others.

The results were as follows.

  • 4.6.3: In App A, //x/admin and ///x/admin returned 200 with the admin screen, and //x/members/secret was not redirected and returned the page with 200. In App B, /admin%2Fsecret, /admin%2fsecret and /members%2Fsecret were not redirected and returned 200, and the catch-all page rendered admin/secret as a single segment. The results of App A's six kinds of requests were the same as for 4.6.3 in round 1.
  • 4.6.4: In App A, the three requests starting with // returned 404. In App B, the three requests containing %2F were redirected (307) to /login. Sending the same six kinds of requests as in round 1 to App A gave the same results as 4.6.3.
  • 4.6.3 with two workarounds: Adding the leading // and encoded slash workarounds below (double-slash.ts and encoded-slash.ts) made requests starting with // and requests containing %2F return 404 in both App A and App B.

However, in App B, which has a catch-all page, the result for //x/admin/secret was different. On 4.6.3 it returned 200, and the redirect rule did not apply. In that case the body rendered on the server was empty and the page content was not exposed (the path recorded in the page data was /admin/secret). We have not checked the behavior when opened in a browser. On 4.6.4 it also returned 200, and the catch-all page rendered /x/admin/secret as four segments. The rule does not match, but the page receives this request as /x/admin/secret, not admin/secret. In other words, "on 4.6.4, requests starting with // return 404" is the result for App A, which has no catch-all page.

What we did not test

We did not verify the // and %2F spellings on 4.6.1, the percent-encoding issue (6gq8), behavior and workarounds on Nuxt 3, or the advisories' statement that "on Nuxt 3 and 4.0 to 4.5.x, // and dot segment requests return 404". We also did not test browser behavior or rules converted into Vercel or Netlify configuration.

Updating, and workarounds if you cannot upgrade

The basic step is to upgrade to 4.6.4 or later (the latest as of November 11). Check the version actually installed with npm ls nuxt. As of November 11, the two additional issues were not yet in the GitHub Advisory Database, and sending 4.6.3 to the npm registry endpoint used by npm audit returned no matching advisories. Do not treat an empty result from npm audit as a reason not to upgrade to 4.6.4.

4.6.4 is a security update that fixes eight Nuxt advisories. The release notes list five behavior changes, such as how error details are handled in production, so read them before upgrading.

If you cannot upgrade, the advisories give the following workarounds.

Case sensitivity issue: Use nuxt.config to make page matching case-sensitive. If existing links or URLs contain uppercase letters, those pages will no longer be found, so check beforehand.

export default defineNuxtConfig({
  router: { options: { sensitive: true } },
})

Dot segment issue: Use server/middleware, which runs before other middleware, to return 404 for requests containing . or .. segments. Middleware runs in file name order, so the name starts with 0..

// server/middleware/0.dot-segments.ts
export default defineEventHandler((event) => {
  if (/(?:^|\/)\.{1,2}(?:\/|$)/.test(event.path.split('?')[0])) {
    throw createError({ statusCode: 404 })
  }
})

The advisory also says that checking getRequestURL(event).pathname instead of event.path is not enough on its own, because percent-encoding remains.

We added these two workarounds to 4.6.1 and ran them in round 1 (with the results described in the previous section). At that time we did not send the // or %2F spellings. According to the advisories' affected ranges, 4.6.0 to 4.6.3 are also affected by the leading // issue, and 4.6.3 and earlier, as well as Nuxt 3, are affected by the encoded slash issue. So if you rely on workarounds, these two are not enough, and you also need the following two.

Leading // issue: Return 404 for requests starting with //.

// server/middleware/double-slash.ts
export default defineEventHandler((event) => {
  if (event.path.startsWith('//')) {
    throw createError({ statusCode: 404 })
  }
})

Encoded slash issue: Return 404 for requests whose path contains %2F. Because this also rejects legitimate requests, such as APIs that accept package names like @scope%2Fname, narrow it if necessary to just the path prefixes protected by your rules.

// server/middleware/encoded-slash.ts
export default defineEventHandler((event) => {
  if (/%2f/i.test(event.path.split('?')[0])) {
    throw createError({ statusCode: 404 })
  }
})

In round 2, adding these two workarounds to 4.6.3 made requests starting with // and requests containing %2F return 404. Ordinary /admin/secret and /members/secret were still redirected (307) to /login, and /admin in App A still returned 401. Placing a reverse proxy or CDN in front that normalizes paths before forwarding is also said to resolve the mismatch.

Rethinking the protection approach itself (editorial suggestion)

From here on is the editorial team's suggestion. What the five issues have in common is the weakness of protecting by URL string. The advisory for the case sensitivity issue states that middleware attached directly to a page (definePageMeta({ middleware: 'auth' })) and checks in the code that fetches page data are not affected. For the dot segment and leading // issues as well, middleware attached with definePageMeta is not affected. On the other hand, the dot segment advisory says that if you check paths in server/middleware, you should also take into account that the page router is case-insensitive.

  • Protect pages that require authentication both with page-side middleware and with checks on the API side that returns the data.
  • If you check paths yourself in server/middleware, also account for uppercase spellings such as /Admin.
  • Add security headers such as Content-Security-Policy to every response, not per route.
  • Add checks to your maintenance inspection procedure that request protected pages using /%2e/admin, //x/admin, %2F and uppercase spellings.

Around the same time, an issue in which the safe handling of useHeadSafe does not work in server islands was also published (GHSA-xfq5-w38q-pc2x, October 16, fixed in 4.6.2, no fix for Nuxt 3). For Nuxt Content (@nuxt/content), three High-severity issues concerning the query API that can be called without authentication (/__nuxt_content/:collection/query) were published on October 21 (including the denial-of-service issue GHSA-xxrh-5hgg-gj4h), and all are fixed in 3.15.2 and later. If your site uses Nuxt Content, check its version as well.

We covered how to handle framework vulnerabilities within a maintenance contract in our article on patching time windows and maintenance contracts. For how to check a Next.js site, see our article on the October Next.js advisories.

Scope of verification

On November 11, 2026, we checked the GitHub Advisory Database, the Security Advisories of the nuxt/nuxt and nuxt/content repositories (retrieved via the GitHub API), the release notes for nuxt/nuxt v4.6.4, the roadmap on nuxt.com, and the npm registry's publication dates and the responses from the npm audit endpoint (a documentation review). On the same day, we also ran a minimal app on localhost twice (hands-on testing; the steps and output are saved in the article's evidence records). In round 1, on Linux with Node.js v22.23.4, we sent six kinds of requests with altered path spellings to Nuxt 4.6.1 and 4.6.3, and to 4.6.1 with the case sensitivity and dot segment workarounds added. In round 2, on macOS (Darwin arm64) with Node.js v22.23.4, we sent requests with the // and %2F spellings added to Nuxt 4.6.3 and 4.6.4, and to 4.6.3 with the leading // and encoded slash workarounds added. In round 2 we also compared an app with a catch-all page. We did not test // and %2F on 4.6.1, the percent-encoding issue, Nuxt 3 and 4.0 to 4.5.x, browser behavior, or deployment targets such as Vercel and Netlify.

GleamHub can help with updating Nuxt sites, migrating from Nuxt 3, and reviewing how members-only pages are protected, through our development, AI and automation consultations. Because the approach depends on how your site is built, please reach out through our contact form.

References

Share this articleXFacebook
Kakeru Suzuki

Fascinated by the possibilities of technology, has had a deep interest in programming and digital art since student days

Turn this article's theme into your company's next step

Starting from what you want to achieve with your website.

We organize user goals, required features, and ongoing maintenance structures to determine the first steps in development and improvement.

  • Website objectives
  • Features and usability
  • Post-launch operations
Consult on web development and improvements

You can consult with us from the initial conceptual stage. Details from this article will be carried over to the inquiry form.

Receive the latest articles via email · Read the web production guide
Free download

Complete Guide to Web Production: Costs, Vendor Selection & Traffic Acquisition [2026 Edition]

We have compiled cost benchmarks, vendor selection criteria, and traffic acquisition strategies into a PDF.

The PDF and newsletter emails are currently in Japanese.

You will also be subscribed to our newsletter. You can unsubscribe at any time.