/admin を開くと 401 が返る。ところが /%61dmin と打つと、管理画面の中身がそのまま表示される。%61 は小文字の「a」をパーセントエンコードしたもので、ブラウザーやサーバーによっては /admin と同じ場所を指します。Next.js の middleware で /admin 配下の認可をしているサイトを、Cloudflare Workers 向けのアダプター @opennextjs/cloudflare で配信している場合、1.20.2 未満の版ではこれが起こり得ます。
2026年10月15日(UTC)、GitHub Advisory Database に GHSA-2vc4-c4r6-p6v6(CVE-2026-100409、重大度 HIGH、CVSS 3.1 で 7.5)が登録されました。対象は @opennextjs/cloudflare の 0.3.0 以上 1.20.2 未満です。修正版の 1.20.2 は2026年7月21日に npm に公開され、opennextjs/opennextjs-cloudflare のリポジトリのアドバイザリも同じ日に公開されていました。npm audit が参照する Advisory Database に入ったのはその3か月近く後です。7月以降に依存を更新していないサイトでは、気づかないまま残っている可能性があります。
本記事は、自社のサイトが当たるかを確かめる手順、編集部の検証環境で再現した結果、更新先の版、middleware だけに認可を任せない設計の見直しを整理したものです。
アドバイザリが挙げる3つの条件
アドバイザリは、次の3つがそろうアプリを対象としています。
enableCacheInterceptionを明示的に有効にしている- アクセス制御を middleware に頼っている
- 保護したいルートの応答がキャッシュに入っている
原因は、パスの扱いの食い違いです。アドバイザリによれば、middleware の照合はエンコードされたままのパス名を使い、キャッシュの横取り(cache interception)はパス名をデコードしてからキャッシュの項目を選んでいました。/%61dmin は matcher の /admin/:path* に一致しないため認可の middleware が動かず、続くキャッシュの検索では /admin として扱われて、保護したページのキャッシュが返ります。/%61dmin/%ZZ のように壊れたエンコードを含むパスでは、区切りごとにデコードしたため、一部だけデコードしたパスでキャッシュを探し続ける問題もあったとしています。
1. enableCacheInterceptionを有効にしているか
open-next.config.ts を開き、defineCloudflareConfig に enableCacheInterception: true があるかを見ます。1.20.2 の型定義では既定値が false です。
// open-next.config.ts(この設定があれば条件1に当たる)
export default defineCloudflareConfig({
incrementalCache: staticAssetsIncrementalCache,
enableCacheInterception: true,
});
既定で無効だからといって、設定していないとは限りません。OpenNext のドキュメントの原稿(opennextjs/docs の pages/cloudflare/caching.mdx)は、キャッシュの設定例として「Small site using revalidation」「Large site using revalidation」「SSG site」の3つを載せており、いずれも enableCacheInterception: true を含んでいます。ガイドの例をそのまま写して作ったサイトは、有効になっている可能性があります。
2. 認可をmiddlewareだけでしているか
middleware.ts の config.matcher と、その中で 401 やログイン画面への転送を返している処理を確かめます。そのうえで、保護したいページ(app/admin/page.tsx など)やルートハンドラーの側でも、セッションや権限を確かめているかを見ます。ページの側に確認がなく、middleware が唯一の関門なら、条件2に当たります。
3. 保護したいルートがキャッシュに入っているか
ビルド後の .next/prerender-manifest.json の routes を見ると、静的に生成されたルートが分かります。保護したいパスがここに並んでいれば、そのページはビルド時に生成されてキャッシュに入ります。ISR のルート(revalidate を指定したページ)も、一度生成されればキャッシュに入ります。
node -e "console.log(Object.keys(require('./.next/prerender-manifest.json').routes))"
あわせて、npm ls @opennextjs/cloudflare で版を確かめます。編集部の検証環境では、1.20.1 を入れた構成で npm audit(npm 10.9.4)を実行すると、GHSA-2vc4-c4r6-p6v6 が high として表示されました。
手元で再現した結果
2026年10月21日、編集部の検証環境(Linux、Node.js v22.22.0)で、アドバイザリの例に沿った最小の App Router のアプリを作りました。/admin は静的なページで本文に PROTECTED-ADMIN-CONTENT を表示し、middleware は matcher /admin/:path* に一致した要求に「Authentication required」と 401 を返します。キャッシュには、ドキュメントの「SSG site」と同じ staticAssetsIncrementalCache を使いました。opennextjs-cloudflare build と populateCache local の後、wrangler dev --local で起動して curl --path-as-is で要求しています。Cloudflare のアカウントへのデプロイはしていません。
最初の構成(/ と /admin だけ)では、1.20.1 でも /%61dmin は 404 になり、すり抜けは起きませんでした。調べると、OpenNext のルーティングが /%61dmin をどのページにも一致しないと判断し、キャッシュを探す前に 404 のページへ切り替えていました。そこで、ルート直下に動的なページ app/[slug]/page.tsx を足すと、結果が変わりました。
| 版(Next.js) | enableCacheInterception | /admin | /%61dmin |
|---|---|---|---|
| 1.20.1(16.2.11) | true | 401 | 200、x-opennext-cache: HIT、保護ページの本文 |
| 1.20.1(16.2.11) | false | 401 | 200、保護ページの本文 |
| 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 は、どの版でも 404 でした。各要求は2回ずつ送り、2回とも同じ結果です。

キャッシュの機能を切るだけでは防げなかった
アドバイザリは、すぐに更新できない場合の回避策の1つ目に「enableCacheInterception: true を外す」ことを挙げています。しかし上の表のとおり、編集部の構成では、1.20.1 で enableCacheInterception を false にしても /%61dmin で保護ページの本文が返りました。このときはキャッシュの横取りの見出し(x-opennext-cache)が付かず、Next.js のサーバーがページを返しています。middleware の照合がエンコードされたパスのままで行われる点は、キャッシュの機能を切っても変わらないためと考えられます。
1.20.2 が依存する @opennextjs/aws 4.1.0 の変更履歴は、この修正(#1199)を「middleware とその照合を、デコードしたパス名でも試す。キャッシュの横取りは壊れたエンコードのパスを扱わない」と説明しています。パッケージの中身を比べても、4.1.0 では middleware の照合に decodeURIComponent した値の判定が加わり、キャッシュの横取りではデコードに失敗したら何もせずに次へ渡すよう変わっていました。
検証は1つの構成、手元の wrangler dev だけで行ったもので、本番の Workers で同じ結果になるかは確かめていません。それでも、ルート直下に [slug] や catch-all のような動的なページがあるサイトでは、キャッシュの設定を切っただけで安心せず、更新を先に進めることを編集部は勧めます。
修正は1.20.2、上げるなら1.20.11以上
この件の修正版は 1.20.2 で、10月21日時点の npm の latest は 10月15日公開の 1.20.12 です。@opennextjs/cloudflare は @opennextjs/aws の版を固定で指定しているため、本体を上げれば修正の入った @opennextjs/aws も入ります。
| 版(npm 公開日) | 同梱する @opennextjs/aws | next の peer の範囲 | wrangler の peer の範囲 |
|---|---|---|---|
| 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 にも peer の範囲の引き上げがあり、Next.js 16 なら 16.2.11 以上が必要です。1.20.11 は Next.js 16.3.8 以上(15系なら 15.5.27 以上)と wrangler 4.148.0 以上を、1.20.12 まで上げる場合は Next.js 16.4.1 以上(15系なら 15.5.28 以上)と、同じく wrangler 4.148.0 以上が求められます。Next.js 16.4.1 は Next.js 自体の10月の脆弱性の修正版でもあり、その内容はNext.js 10月の脆弱性9件の記事で整理しています。編集部は、Next.js と OpenNext をそろえて最新の版に上げ、ステージングで確かめてから本番に出す進め方を提案します。
1.20.2 には、ほかにも @opennextjs/aws 4.1.0 の変更が入っています。変更履歴によれば、トップページ(/)のキャッシュの読み出しの修正も含まれており、編集部の検証でも、1.20.1 では / に x-opennext-cache が付かず、1.20.2 では HIT が付きました。更新後にトップページの応答の見出しが変わっても、異常ではありません。
1.20.2で止めない:middlewareのヘッダー削除の件
1.20.2〜1.20.10 が同梱する @opennextjs/aws 4.1.0〜4.1.9 には、別の問題があります。opennextjs/opennextjs-aws のリポジトリが10月13日に公開したアドバイザリ GHSA-2mvv-9g7g-pfcg(重大度 high、CVSS 3.1 で 7.4、CVE なし)です。middleware が NextResponse.next() に渡す要求ヘッダーから利用者の送ったヘッダーを削除しても、OpenNext では削除したはずのヘッダーがアプリに届いていました。アドバイザリは、middleware で本人確認や権限のヘッダーを消し、ページやバックエンドが「そのヘッダーがないこと」を前提に判断しているアプリでは、認証や認可をすり抜けられる可能性があるとしています。middleware でヘッダーを削除していないアプリは対象外です。
対象は @opennextjs/aws 4.1.9 以下、修正版は 4.1.10 で、@opennextjs/cloudflare は 1.20.11 で同梱の版を 4.1.10 に上げました。修正は、今回の件と同じ middleware の処理のファイル(core/routing/middleware.js)に入っています。@opennextjs/cloudflare 側にはアドバイザリが出ておらず、10月21日の時点で Advisory Database にも入っていないため、npm audit には出ません。この件は編集部では検証していませんが、middleware でヘッダーを削除しているかどうかにかかわらず、今回の件の修正版の 1.20.2 で止めず、1.20.11 以上(実際には最新の 1.20.12)に上げることを勧めます。
なお、GHSA-2vc4-c4r6-p6v6 の対象に挙がっているのは @opennextjs/cloudflare だけです。AWS 向けに @opennextjs/aws を直接使う構成は対象に含まれておらず、npm audit にも出ません。修正そのものは @opennextjs/aws 4.1.0 に入っているため、AWS 向けの構成も、ヘッダーの件の修正を含む 4.1.10 以上かを確かめておくと安全です。
middlewareだけで守らない設計に直す
ここからは編集部の提案です。今回の問題は、「同じページを指すURLが1つではない」ことと、「認可を通り道の1か所だけで行っていた」ことが重なって起きました。版を上げたうえで、次の点も見直します。
保護したいページを静的に生成しない
認可が必要なページを静的に生成すると、その中身は認可と無関係にキャッシュに置かれます。今回の検証でも、/admin は静的なページだったため、キャッシュから本文がそのまま返りました。ログインした人だけが見る管理画面や会員ページは、要求ごとにサーバーで描画する(cookie やヘッダーを読む)作りにし、ページの側でもセッションを確かめます。
ページとルートハンドラーでも権限を確かめる
アドバイザリの回避策の2つ目も、「middleware だけに頼らず、保護したルートのハンドラーの中で認可を行う」ことです。Server Component、Server Action、Route Handler の入口でセッションを確かめる共通の関数を用意し、middleware は「未ログインならログイン画面へ送る」という利便のための関門と位置づけます。認証の仕組みを引き継いだときの確かめ方は、JWT検証ミドルウェアの落とし穴の記事でも扱っています。
エンコードしたパスを手前で拒否する
回避策の3つ目は、信頼できるプロキシで、保護したルートへのエンコードされたパスを Worker に届く前に拒否することです。アドバイザリは、matcher にエンコードした別名を足すだけでは不完全だとしています。同じパスを表すエンコードの仕方が複数あるためです。手前で拒否する場合も、更新までのつなぎとして扱います。
確認した範囲
2026年10月21日に、github/advisory-database の GHSA-2vc4-c4r6-p6v6 の JSON、opennextjs/opennextjs-cloudflare と opennextjs/opennextjs-aws の変更履歴(main)、npm に公開された @opennextjs/cloudflare 1.20.1・1.20.2 と @opennextjs/aws 4.0.2・4.1.0 のパッケージの中身、opennextjs/docs のキャッシュのガイドの原稿を確認しました(資料調査)。あわせて、最小の App Router のアプリを @opennextjs/cloudflare 1.20.1・1.20.2(Next.js 16.2.11、wrangler 4.86.0)と 1.20.12(Next.js 16.4.2、wrangler 4.151.3)でビルドし、
wrangler dev --localで/admin・/%61dmin・/%61dmin/%ZZへの応答を比べ、1.20.1 でnpm auditを実行しました(実機検証)。Cloudflare へのデプロイと本番の Workers での動作、R2 や Durable Objects を使うキャッシュの構成、Pages Router、ISR のルート、Next.js 15系、Next.js 16 の proxy.ts での動作、GHSA-2mvv-9g7g-pfcg の挙動と 1.20.11 の構成は確かめていません。公開前の10月21日には、GitHub の API で opennextjs/opennextjs-cloudflare と opennextjs/opennextjs-aws のリポジトリのアドバイザリ、Advisory Database の登録内容、NVD の CVE-2026-100409、npm の最新の公開記録、opennext.js.org のキャッシュのページも確認しました。
Cloudflare 上の Next.js のサイトの更新計画や、認可の作りの見直しは、開発・AI・自動化のご相談からお問い合わせください。Vercel 以外で Next.js を動かす方法の比べ方は、vinext 1.0の記事で整理しています。
参考資料
- GHSA-2vc4-c4r6-p6v6: OpenNext Cloudflare: Cache interception can bypass middleware protection for encoded paths(github/advisory-database)
- GHSA-2vc4-c4r6-p6v6(opennextjs/opennextjs-cloudflare の security advisory)
- @opennextjs/cloudflare の CHANGELOG(1.20.2: fix: handle encoded middleware and cache paths safely)
- GHSA-2mvv-9g7g-pfcg: Middleware header deletion is ignored(opennextjs/opennextjs-aws の security advisory)
- @opennextjs/aws の CHANGELOG(4.1.0: #1199、#1189、4.1.10: ヘッダー削除の修正)
- opennextjs/opennextjs-cloudflare — commit 38ea40b
- opennextjs/opennextjs-aws — commit 7d9ab24(#1199)
- OpenNext のドキュメントの原稿 — Caching(opennextjs/docs、pages/cloudflare/caching.mdx)
- @opennextjs/cloudflare — npm registry









