メインコンテンツへスキップ
← 記事一覧に戻る
·開発·9 min read

Next.js 16 破壊的変更まとめ — middleware→proxy 移行ガイド

recipe-aiNext.js移行ガイド個人開発

Next.js 16 にアップグレードしたら、middleware.ts がまるごと動かなくなった。ビルドは通るのにルーティングが効かない。ドキュメントを探しても公式サイトには情報が少なく、結局 node_modules/next/dist/docs/ の中に答えがあった。

この記事は、筆者が recipe-ai(YouTube 料理動画からレシピを AI 抽出するアプリ)を Next.js 16 で新規構築する際に実際に遭遇した破壊的変更と、その対処法をまとめたものです。

この記事でわかること:

  • Next.js 16 で middleware.ts が廃止され proxy.ts に変わった理由と移行手順
  • export 関数名の変更(middleware から proxy または default
  • next-intl + Supabase Auth を統合した proxy.ts の実装例
  • node_modules/next/dist/docs/ にある公式ドキュメントの活用方法
  • 移行時に確認すべきチェックリスト

最大の変更: middleware.ts から proxy.ts へ

なぜ名前が変わったのか

Next.js の公式ドキュメント(node_modules/next/dist/docs/01-app/03-api-reference/03-file-conventions/proxy.md)にはこう書かれています。

The term "middleware" often confuses users with Express.js middleware, which can encourage misuse. To clarify our direction, we are renaming the file convention to "proxy."

要するに、Express.js の middleware と混同されやすく、本来の用途を超えた使い方が広がってしまったため、「proxy」という名前に変えて役割を明確にした、ということです。

移行手順

移行自体は単純で、2つの変更だけで済みます。

1. ファイル名を変更する

mv middleware.ts proxy.ts

2. export する関数名を変更する

- export function middleware(request: NextRequest) {
+ export function proxy(request: NextRequest) {

default export でも動作します。

export default function proxy(request: NextRequest) {
  // ...
}

config オブジェクトの書き方は変わりません。matcher パターンもそのまま使えます。

公式 codemod もある

Next.js は移行用の codemod を提供しています。

npx @next/codemod@canary middleware-to-proxy .

これを実行すると、ファイル名のリネームと関数名の変更が自動で行われます。ただし、筆者の環境(recipe-ai)では手動で2箇所直すだけだったので codemod は使いませんでした。

Before / After

Before(Next.js 15 以前: middleware.ts)

import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";

export function middleware(request: NextRequest) {
  return NextResponse.redirect(new URL("/home", request.url));
}

export const config = {
  matcher: "/about/:path*",
};

After(Next.js 16: proxy.ts)

import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";

export function proxy(request: NextRequest) {
  return NextResponse.redirect(new URL("/home", request.url));
}

export const config = {
  matcher: "/about/:path*",
};

差分はファイル名と関数名だけ。NextRequestNextResponseconfig.matcher の API は変わっていません。


proxy.ts の実装例 -- recipe-ai の場合

recipe-ai では proxy.ts で以下の2つの機能を実現しています。

  1. next-intl による locale ルーティング(現在は日本語のみ)
  2. Supabase Auth による認証保護/recipes ルートはログイン必須)

実際のコードはこうなっています。

import createMiddleware from "next-intl/middleware";
import { updateSession } from "@/lib/supabase/middleware";
import { type NextRequest, NextResponse } from "next/server";
import { routing } from "./i18n/routing";

const intlMiddleware = createMiddleware(routing);

const protectedRoutes = ["/recipes"];

export async function proxy(request: NextRequest) {
  const { pathname } = request.nextUrl;
  const pathnameWithoutLocale = pathname.replace(/^\/ja/, "");

  const isProtected = protectedRoutes.some((route) =>
    pathnameWithoutLocale.startsWith(route)
  );

  if (isProtected) {
    const { supabaseResponse, user } = await updateSession(request);
    if (!user) {
      return NextResponse.redirect(new URL(`/ja/login`, request.url));
    }
    return supabaseResponse;
  }

  return intlMiddleware(request);
}

export const config = {
  matcher: ["/((?!api|_next/static|_next/image|favicon.ico|.*\\..*).*)"],
};

ポイント解説

protectedRoutes の分離: 認証が必要なルートを配列で管理しています。/recipes にアクセスした場合だけ Supabase の updateSession でセッションを検証し、未ログインなら /ja/login にリダイレクトします。

locale の除去: pathname.replace(/^\/ja/, "") で locale プレフィックスを取り除いてからルート判定しています。next-intl を使うと URL が /ja/recipes のようになるため、この処理がないと protectedRoutes のマッチが効きません。

next-intl との共存: 認証保護が不要なルートでは intlMiddleware(request) をそのまま返します。next-intl の createMiddleware は内部的にはまだ middleware という名前を使っていますが、proxy.ts から呼ぶ分には問題ありません。ライブラリ内部の関数名と Next.js のファイル規約は別物です。

updateSession の役割: lib/supabase/middleware.ts で定義されている関数で、Supabase のセッション Cookie を更新しつつ、現在のユーザー情報を返します。認証チェックとセッション更新を1回のリクエストで済ませるパターンです。


その他の破壊的変更

node_modules/next/dist/docs/ を読む習慣をつける

Next.js 16 では公式ドキュメントが node_modules/next/dist/docs/ にバンドルされています。ブラウザで Next.js の公式サイトを見ても最新情報が反映されていないことがあるため、手元の node_modules 内のドキュメントを直接確認するのが確実です。

ディレクトリ構成はこうなっています。

node_modules/next/dist/docs/
  01-app/           # App Router 関連
  02-pages/         # Pages Router 関連
  03-architecture/  # 内部アーキテクチャ
  04-community/     # コミュニティ
  index.md

proxy 関連のドキュメントは以下にあります。

node_modules/next/dist/docs/01-app/03-api-reference/03-file-conventions/proxy.md

App Router 関連の注意点

Next.js 16 は App Router が前提です。Pages Router は引き続きサポートされていますが、新しい API や規約は App Router 側に追加されています。

proxy.ts 以外にも変更が入っている可能性があるため、新規プロジェクトを始める際は node_modules/next/dist/docs/01-app/ 配下を一通り確認することをおすすめします。特に以下のファイルは目を通しておくと安心です。

  • 03-api-reference/03-file-conventions/ -- ファイル規約の変更
  • 03-api-reference/01-directives/ -- ディレクティブの変更
  • 03-api-reference/04-functions/ -- 関数 API の変更

移行チェックリスト

Next.js 15 以前から Next.js 16 へ移行する際に確認すべき項目をまとめます。

  • middleware.tsproxy.ts にリネームしたか
  • export 関数名を middleware から proxy(または default)に変更したか
  • config.matcher のパターンはそのまま動作するか(API は変更なし)
  • next-intl など、middleware を内部で使うライブラリとの互換性を確認したか
  • Supabase Auth の updateSession など、セッション管理系の処理が正常に動作するか
  • node_modules/next/dist/docs/ で他の破壊的変更がないか確認したか
  • ビルド(npm run build)がエラーなく通るか
  • 認証保護されたルートにログイン/未ログイン状態でアクセスして動作確認したか

まとめ

Next.js 16 の最大の破壊的変更は middleware.ts から proxy.ts へのリネーム。移行自体はファイル名と関数名を変えるだけで完了する。困ったら node_modules/next/dist/docs/ を読むのが最も確実。


recipe-ai を試す(無料)

この記事で作っている YouTube 料理レシピ抽出アプリ本体。動画 URL を貼るだけで AI がレシピに変換。月 5 本まで無料。

関連記事

Gemini 2.5 Flash で YouTube動画からレシピを自動抽出する方法

recipe-aiの技術スタックとAI実装の全容

Supabase 1プロジェクト × 複数アプリ構成

RLSで安全に共用する方法

Claude Crew Lab Free — 毎月の実験記録をメールで

Claude Code × 個人開発のリアルな事故・発見・SaaS アイデアを毎月第1月曜にお届け。登録で「収益化チェックリスト 15 項目」を無料プレゼント。

Lab Free 登録(月1回・無料)

この記事が役に立ったらシェア