Opening /admin returns 401. But typing /%61dmin displays the admin page content as is. %61 is the lowercase "a" percent-encoded, and depending on the browser or server it points to the same place as /admin. If a site that performs authorization for /admin in Next.js middleware is served with @opennextjs/cloudflare, the adapter for Cloudflare Workers, this can happen in versions below 1.20.2.
On October 15, 2026 (UTC), GHSA-2vc4-c4r6-p6v6 (CVE-2026-100409, severity HIGH, CVSS 3.1 score 7.5) was added to the GitHub Advisory Database. It affects @opennextjs/cloudflare from 0.3.0 up to, but not including, 1.20.2. The fixed version 1.20.2 was published to npm on July 21, 2026, and the advisory in the opennextjs/opennextjs-cloudflare repository was published the same day. It entered the Advisory Database that npm audit references nearly three months later. Sites that have not updated dependencies since July may still be affected without noticing.
This article covers the steps to check whether your site is affected, the results of reproducing the issue in the editorial team's test environment, which version to update to, and how to revisit the design so that authorization is not left to middleware alone.
The three conditions listed in the advisory
The advisory targets apps where all three of the following apply.
enableCacheInterceptionis explicitly enabled- Access control relies on middleware
- Responses for the routes you want to protect are in the cache
The cause is a mismatch in how paths are handled. According to the advisory, middleware matching used the still-encoded pathname, while cache interception decoded the pathname before selecting a cache entry. /%61dmin does not match the matcher /admin/:path*, so the authorization middleware does not run, and the subsequent cache lookup treats it as /admin, returning the cached protected page. The advisory also says that for paths containing malformed encoding such as /%61dmin/%ZZ, decoding was done segment by segment, so there was a further problem of continuing to look up the cache with a partially decoded path.
1. Is enableCacheInterception enabled?
Open open-next.config.ts and check whether defineCloudflareConfig contains enableCacheInterception: true. In the 1.20.2 type definitions, the default is false.
// open-next.config.ts(この設定があれば条件1に当たる)
export default defineCloudflareConfig({
incrementalCache: staticAssetsIncrementalCache,
enableCacheInterception: true,
});
Being disabled by default does not mean you have not set it. The source of the OpenNext documentation (pages/cloudflare/caching.mdx in opennextjs/docs) lists three cache configuration examples, "Small site using revalidation", "Large site using revalidation" and "SSG site", and all of them include enableCacheInterception: true. Sites built by copying the guide's examples as is may have it enabled.
2. Is authorization done only in middleware?
Check config.matcher in middleware.ts and the logic inside it that returns 401 or redirects to the login page. Then check whether the pages you want to protect (such as app/admin/page.tsx) and route handlers also verify the session or permissions themselves. If the pages have no check and middleware is the only gate, condition 2 applies.
3. Are the routes you want to protect in the cache?
Looking at routes in .next/prerender-manifest.json after the build shows which routes were statically generated. If the paths you want to protect are listed there, those pages are generated at build time and placed in the cache. ISR routes (pages that specify revalidate) also enter the cache once they have been generated.
node -e "console.log(Object.keys(require('./.next/prerender-manifest.json').routes))"
Also check the version with npm ls @opennextjs/cloudflare. In the editorial team's test environment, running npm audit (npm 10.9.4) on a setup with 1.20.1 installed reported GHSA-2vc4-c4r6-p6v6 as high.
Results of reproducing it locally
On October 21, 2026, in the editorial team's test environment (Linux, Node.js v22.22.0), we built a minimal App Router app following the advisory's example. /admin is a static page that displays PROTECTED-ADMIN-CONTENT in its body, and the middleware returns "Authentication required" with 401 for requests matching the matcher /admin/:path*. For the cache, we used staticAssetsIncrementalCache, the same as the documentation's "SSG site". After opennextjs-cloudflare build and populateCache local, we started it with wrangler dev --local and sent requests with curl --path-as-is. We did not deploy to a Cloudflare account.
In the first setup (only / and /admin), /%61dmin returned 404 even on 1.20.1, and no bypass occurred. On investigation, OpenNext's routing determined that /%61dmin did not match any page and switched to the 404 page before looking up the cache. So we added a dynamic page app/[slug]/page.tsx directly under the root, and the result changed.
| Version (Next.js) | enableCacheInterception | /admin | /%61dmin |
|---|---|---|---|
| 1.20.1(16.2.11) | true | 401 | 200, x-opennext-cache: HIT, protected page body |
| 1.20.1(16.2.11) | false | 401 | 200, protected page body |
| 1.20.2(16.2.11) | true | 401 | 401 |
| 1.20.2(16.2.11) | false | 401 | 401 |
| 1.20.12(16.4.2) | true | 401 | 401 |
/%61dmin/%ZZ returned 404 on every version. Each request was sent twice, with the same result both times.

Turning off the cache feature alone did not prevent it
The advisory lists "remove enableCacheInterception: true" as the first workaround if you cannot update right away. However, as the table above shows, in the editorial team's setup, even with enableCacheInterception set to false on 1.20.1, /%61dmin returned the protected page body. In this case the cache interception header (x-opennext-cache) was not present, and the Next.js server returned the page. We believe this is because middleware matching is still done on the encoded path even when the cache feature is turned off.
The changelog of @opennextjs/aws 4.1.0, which 1.20.2 depends on, describes this fix (#1199) as "also try middleware and its matching against the decoded pathname; cache interception does not handle paths with malformed encoding". Comparing the package contents, 4.1.0 adds a check of the decodeURIComponent value to middleware matching, and cache interception was changed to do nothing and pass the request on if decoding fails.
The test was done with a single setup, using only wrangler dev locally, and we have not verified whether production Workers behave the same way. Even so, for sites with dynamic pages such as [slug] or a catch-all directly under the root, the editorial team recommends that you not feel safe just because the cache setting is off, and move ahead with the update.
Fixed in 1.20.2; if upgrading, go to 1.20.11 or later
The fixed version for this issue is 1.20.2, and as of October 21 the npm latest is 1.20.12, published October 15. Because @opennextjs/cloudflare pins the version of @opennextjs/aws, upgrading the main package also installs the fixed @opennextjs/aws.
| Version (npm release date) | Bundled @opennextjs/aws | next peer range | wrangler peer range |
|---|---|---|---|
| 1.20.1(2026-06-26) | 4.0.2 | >=15.5.18 <16 || >=16.2.6 | ^4.86.0 |
| 1.20.2(2026-07-21) | 4.1.0 | >=15.5.21 <16 || >=16.2.11 | ^4.86.0 |
| 1.20.11(2026-10-13) | 4.1.10 | >=15.5.27 <16 || >=16.3.8 | ^4.148.0 |
| 1.20.12(2026-10-15) | 4.1.11 | >=15.5.28 <16 || >=16.4.1 | ^4.148.0 |
1.20.2 also raises the peer ranges: with Next.js 16, 16.2.11 or later is required. 1.20.11 requires Next.js 16.3.8 or later (15.5.27 or later on the 15 line) and wrangler 4.148.0 or later, and going up to 1.20.12 requires Next.js 16.4.1 or later (15.5.28 or later on the 15 line) and likewise wrangler 4.148.0 or later. Next.js 16.4.1 is also the fix release for Next.js's own October vulnerabilities, which are covered in our article on Next.js's 9 October vulnerabilities. The editorial team suggests upgrading Next.js and OpenNext together to the latest versions, verifying on staging, and then releasing to production.
1.20.2 also includes other changes from @opennextjs/aws 4.1.0. According to the changelog, these include a fix for reading the cache for the top page (/), and in the editorial team's testing as well, / did not have x-opennext-cache on 1.20.1, while HIT was present on 1.20.2. If the headers in the top page's response change after updating, that is not a malfunction.
Do not stop at 1.20.2: the middleware header deletion issue
@opennextjs/aws 4.1.0 to 4.1.9, bundled with 1.20.2 to 1.20.10, has a separate problem: advisory GHSA-2mvv-9g7g-pfcg (severity high, CVSS 3.1 score 7.4, no CVE), published on October 13 by the opennextjs/opennextjs-aws repository. Even when middleware deleted user-supplied headers from the request headers passed to NextResponse.next(), in OpenNext the supposedly deleted headers still reached the app. The advisory says that apps where middleware strips identity or permission headers, and where pages or the backend make decisions assuming "that header is absent", may allow authentication or authorization to be bypassed. Apps whose middleware does not delete headers are not affected.
Affected are @opennextjs/aws 4.1.9 and earlier, the fixed version is 4.1.10, and @opennextjs/cloudflare raised its bundled version to 4.1.10 in 1.20.11. The fix is in the same middleware handling file (core/routing/middleware.js) as the issue in this article. No advisory has been issued on the @opennextjs/cloudflare side, and as of October 21 it is not in the Advisory Database either, so it does not appear in npm audit. The editorial team has not tested this issue, but regardless of whether your middleware deletes headers, we recommend that you not stop at 1.20.2, the fix for this article's issue, and upgrade to 1.20.11 or later (in practice, the latest 1.20.12).
Note that only @opennextjs/cloudflare is listed as affected by GHSA-2vc4-c4r6-p6v6. Setups that use @opennextjs/aws directly for AWS are not included and do not appear in npm audit. Since the fix itself is in @opennextjs/aws 4.1.0, it is safer to also check that AWS setups are on 4.1.10 or later, which includes the fix for the header issue.
Redesign so middleware is not the only protection
From here on are the editorial team's suggestions. This problem arose from a combination of "there is more than one URL pointing to the same page" and "authorization was done at only one point along the path". After upgrading, also review the following points.
Do not statically generate pages you want to protect
If you statically generate pages that require authorization, their content is placed in the cache regardless of authorization. In this test too, /admin was a static page, so its body was returned straight from the cache. Build admin screens and member pages that only logged-in users see so they are rendered on the server per request (reading cookies or headers), and verify the session on the page side as well.
Check permissions in pages and route handlers too
The advisory's second workaround is also to "perform authorization inside the handlers of protected routes rather than relying only on middleware". Prepare a shared function that verifies the session at the entry point of Server Components, Server Actions and Route Handlers, and treat middleware as a convenience gate that "sends users who are not logged in to the login page". How to check an authentication setup you have inherited is also covered in our article on the pitfalls of JWT verification middleware.
Reject encoded paths in front
The third workaround is to reject encoded paths to protected routes at a trusted proxy before they reach the Worker. The advisory says that simply adding encoded aliases to the matcher is incomplete, because there are multiple ways to encode the same path. Even if you reject them in front, treat this as a stopgap until you update.
Scope of verification
On October 21, 2026, we checked the JSON for GHSA-2vc4-c4r6-p6v6 in github/advisory-database, the changelogs (main) of opennextjs/opennextjs-cloudflare and opennextjs/opennextjs-aws, the contents of the @opennextjs/cloudflare 1.20.1 and 1.20.2 and @opennextjs/aws 4.0.2 and 4.1.0 packages published on npm, and the source of the caching guide in opennextjs/docs (desk research). In addition, we built a minimal App Router app with @opennextjs/cloudflare 1.20.1 and 1.20.2 (Next.js 16.2.11, wrangler 4.86.0) and 1.20.12 (Next.js 16.4.2, wrangler 4.151.3), compared the responses to
/admin,/%61dminand/%61dmin/%ZZwithwrangler dev --local, and rannpm auditon 1.20.1 (hands-on testing). We did not verify deployment to Cloudflare or behavior on production Workers, cache setups using R2 or Durable Objects, the Pages Router, ISR routes, the Next.js 15 line, behavior with proxy.ts in Next.js 16, the behavior of GHSA-2mvv-9g7g-pfcg, or the 1.20.11 setup. On October 21, before publication, we also checked the advisories in the opennextjs/opennextjs-cloudflare and opennextjs/opennextjs-aws repositories via the GitHub API, the Advisory Database entry, CVE-2026-100409 on NVD, the latest npm release records, and the caching page on opennext.js.org.
For update plans for Next.js sites on Cloudflare or reviews of how authorization is built, please contact us via Development, AI and automation inquiries. How to compare ways of running Next.js outside Vercel is covered in our article on vinext 1.0.
References
- GHSA-2vc4-c4r6-p6v6: OpenNext Cloudflare: Cache interception can bypass middleware protection for encoded paths(github/advisory-database)
- GHSA-2vc4-c4r6-p6v6 (security advisory for opennextjs/opennextjs-cloudflare)
- @opennextjs/cloudflare CHANGELOG (1.20.2: fix: handle encoded middleware and cache paths safely)
- GHSA-2mvv-9g7g-pfcg: Middleware header deletion is ignored (security advisory for opennextjs/opennextjs-aws)
- @opennextjs/aws CHANGELOG (4.1.0: #1199, #1189; 4.1.10: header deletion fix)
- opennextjs/opennextjs-cloudflare — commit 38ea40b
- opennextjs/opennextjs-aws — commit 7d9ab24(#1199)
- Source of the OpenNext documentation: Caching (opennextjs/docs, pages/cloudflare/caching.mdx)
- @opennextjs/cloudflare — npm registry









