Next.js ミドルウェアでの matcher の役割と動作を解説

Next.js でミドルウェアを活用するとき、config オブジェクトの matcher プロパティが重要な役割を果たします。本記事では、この matcher の仕組みや、withAuth と組み合わせた場合の動作について、初心者にもわかりやすく解説します。また、NextAuthMiddlewareOptions を参考に、withAuth の具体的な挙動についても触れていきます。


middleware.tsmatcher の基本動作

Next.js の middleware.ts は、リクエストがサーバーに到達する前に実行される特別な処理を記述する場所です。
config.matcher プロパティを設定することで、どのリクエストにミドルウェアを適用するかを制御できます。

例として、以下の設定を見てみましょう:

export const config = {
matcher: ['/dashboard/:path', '/editor/:path', '/login', '/register'],
}
  • matcher で指定されたパスのみ対象
    この例では、/dashboard/:path, /editor/:path, /login, /register にリクエストが送られた場合のみ、ミドルウェアが実行されます。
  • matcher に指定されていないパスは対象外
    /about/api/data など、matcher に含まれていないパスはミドルウェアをスキップし、直接リクエストが処理されます。

withAuth の動作と authorized コールバック

NextAuth.js の withAuth を利用すると、認証状態に基づいてリクエストを制御できます。このとき、authorized コールバックが重要な役割を果たします。

基本例

以下は、特定の条件でリクエストを制限する例です:

export default withAuth({
callbacks: {
authorized: ({ token }) => !!token, // JWT トークンが存在すればアクセス許可
},
});
  • authorized コールバック
    authorized は、リクエストを許可するかどうかを決定する関数です。この関数が true を返すと、リクエストがそのまま進行します。false を返す場合、認証ページ(デフォルトでは /api/auth/signin)にリダイレクトされます。
  • トークンの検証
    authorized: ({ token }) => !!token では、トークンが存在する場合に true を返しています。つまり、認証済みのリクエストだけが許可されます。

matcher が動作に与える影響

matcher は、ミドルウェアを実行するリクエストを選別するため、認証が必要なページや不要なページを効率よく制御できます。

認証前後のページに matcher を設定する理由

  1. 認証前のページ (/login, /register)
    認証済みユーザーがこれらのページにアクセスした場合、再度ログインフォームを表示するのは不自然です。ミドルウェアで /dashboard にリダイレクトすることで、自然な動作を実現します。
  2. 認証後のページ (/dashboard, /editor)
    未認証ユーザーがこれらのページにアクセスすると、セキュリティ的に問題があります。ミドルウェアで /login にリダイレクトすることで、アクセス制限を確実にします。

matcher を使った効率的な制御

matcher を設定することで、ミドルウェアを適用する範囲を必要最小限に抑えることができます。

  • パフォーマンス向上
    不要なリクエストにミドルウェアを適用しないため、処理が軽くなります。
  • 意図した動作の保証
    /api_next/static など、認証と無関係なリソースに影響を与えないため、予期せぬエラーが減ります。

実例: middleware.ts 全体のコード

以下は、認証ページと認証後ページを適切に制御するミドルウェアの例です:

import { withAuth } from 'next-auth/middleware';
import { NextResponse } from 'next/server';

export default withAuth(
async function middleware(req) {
const { pathname } = req.nextUrl;

// 認証済みのトークンを取得
const token = await getToken({ req });

const isAuth = !!token;
const isAuthPage = pathname.startsWith('/login') || pathname.startsWith('/register');

// 認証済みのユーザーがログインページにアクセスした場合
if (isAuthPage && isAuth) {
return NextResponse.redirect(new URL('/dashboard', req.url));
}

// 未認証ユーザーが保護されたページにアクセスした場合
if (!isAuth && !isAuthPage) {
return NextResponse.redirect(new URL('/login', req.url));
}
},
{
callbacks: {
authorized: ({ token }) => !!token, // 認証済みかどうかをチェック
},
}
);

export const config = {
matcher: ['/dashboard/:path', '/editor/:path', '/login', '/register'],
};

まとめ

matcher は、ミドルウェアを効率的に適用するためのフィルターです。特定のパスにのみ認証ロジックを適用することで、不要な処理を減らし、意図した動作を保証できます。

  • 認証前のページを含める理由:認証済みユーザーが不要なログインページを見ないようにするため。
  • 認証後のページを含める理由:未認証ユーザーがアクセスできないようにするため。

これを活用することで、Next.js アプリケーションの認証機能を安全かつ効率的に構築できます。