1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

🔀 Next.js Middleware で `/admin` を 1 行リダイレクトする最速ハック

1
Posted at

TL;DR

  • middleware.tsNextResponse.redirect() を返すだけ。next.config.jsuseEffect も不要
  • matcher で対象パスを絞れば静的ファイルへの不要な介入を防げる
  • 307 (一時) か 308 (永続) かは用途次第で使い分け

背景・課題

Next.js App Router で SaaS を開発していると、こんなケースに出くわすことがあります。

LP 用のサブドメイン (app.example.com) に /admin でアクセスされたとき、
ログイン画面 /login に飛ばしたい。

要件としては単純ですが、「どこに書けばいいか」で迷う人は多いです。


やりがちな解法とその欠点

パターン 1: next.config.jsredirects()

// next.config.js
module.exports = {
  async redirects() {
    return [
      {
        source: '/admin',
        destination: '/login',
        permanent: false,
      },
    ]
  },
}

欠点: redirects() はビルド時に静的評価されるため、動的な条件分岐ができません。「Cookie の有無で分岐」「特定のヘッダーがある場合のみ」といったロジックを挟めないので、実運用では使いにくい場面が多いです。

パターン 2: app/admin/page.tsx から useEffect + router.push

// app/admin/page.tsx (NG 例)
'use client'
import { useEffect } from 'react'
import { useRouter } from 'next/navigation'

export default function AdminPage() {
  const router = useRouter()
  useEffect(() => {
    router.push('/login')
  }, [router])
  return null
}

欠点:

  1. CLS (Cumulative Layout Shift) が発生する: ページが一瞬レンダーされてから遷移するため、Lighthouse スコアを下げます
  2. SEO に不利: HTML が先に返ってからリダイレクトするため、クローラーが混乱しやすい
  3. 余分なページコンポーネントが必要: リダイレクトだけのために tsx ファイルを作るのは無駄

正解: Middleware で 1 行

ファイル配置

middleware.ts はプロジェクトルート (または src/ 配下) に置きます。

my-app/
├── app/
│   └── ...
├── middleware.ts   ← ここ
└── next.config.js

実装コード

// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
  return NextResponse.redirect(new URL('/login', request.url))
}

export const config = {
  matcher: '/admin/:path*',
}

これだけです。 本当に 1 行 (+ matcher 設定) で完結します。

各行の解説

意味
NextResponse.redirect(new URL('/login', request.url)) リクエスト元の origin を維持しつつ /login へ 307 リダイレクト
matcher: '/admin/:path*' /admin および /admin/settings 等のサブパスにのみ Middleware を適用

request.url を base にして new URL('/login', request.url) とすることで、https://app.example.com/login のように 自動的に origin が補完されます。ハードコードが不要なのが嬉しいポイントです。

matcher を設定する理由

matcher を省略すると、_next/static/_next/image/ などの静的ファイルリクエストにも Middleware が走り、パフォーマンスが劣化します。対象パスを明示的に絞るのがベストプラクティスです。


307 vs 308 の使い分け

NextResponse.redirect() はデフォルトで 307 Temporary Redirect を返します。用途に応じて使い分けましょう。

ステータス 意味 使うべき場面
307 Temporary Redirect 一時的な転送。POST リクエストのメソッドを維持 認証ガード・仮のリダイレクト
308 Permanent Redirect 永続的な転送。POST リクエストのメソッドを維持 URL 変更が確定した場合
301 Moved Permanently 永続的な転送。POST が GET に変わることがある SEO 目的の URL 統一 (注意が必要)
302 Found 一時的な転送。POST が GET に変わることがある 一般的な一時リダイレクト

認証ガードとして /admin/login に使う場合は 307 が適切です。「まだ未認証だから一時的にログインページに飛ばす」という意味合いで、将来的に認証後は /admin に戻れるからです。

ステータスコードを変更するには、第 2 引数でオプションを渡します:

return NextResponse.redirect(new URL('/login', request.url), {
  status: 308,
})

動作確認: curl で検証

ローカル (next dev) を起動した状態で試してみましょう。

# -L でリダイレクトを追跡、-v で詳細表示
curl -Lv http://localhost:3000/admin 2>&1 | grep -E "< HTTP|Location"

期待される出力:

< HTTP/1.1 307 Temporary Redirect
< Location: /login
...
< HTTP/1.1 200 OK

/admin に対して 307 が返り、/login で 200 が得られれば成功です。


Lighthouse への影響 (一般論)

方式 FCP CLS SEO
useEffect + router.push 遅め 悪化しやすい 不利
next.config.jsredirects() 速い 影響なし
Middleware 最速 影響なし

Middleware はリクエストがルートレンダリングに到達するに処理されるため、ブラウザに HTML が届く前にリダイレクトレスポンスが返ります。CLS は原理的に発生せず、Lighthouse の Performance・SEO スコアにも悪影響を与えません。


応用: サブドメインで条件分岐

同じパターンを応用すると、サブドメインによって処理を分岐させることもできます。

// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
  const hostname = request.headers.get('host') ?? ''

  // 管理用サブドメインからのアクセスのみ /login にリダイレクト
  if (hostname.startsWith('admin.')) {
    return NextResponse.redirect(new URL('/login', request.url))
  }

  return NextResponse.next()
}

export const config = {
  matcher: '/:path*',
}

next.config.js の静的な redirects() では書けない、動的ロジックが光るユースケースです。


Next.js v16 の注意点: middleware.tsproxy.ts

Next.js v16.0.0 から、middleware.tsproxy.ts に改名される予定です (2026 年 5 月時点のドキュメントより)。現在は公式 codemod でマイグレーション可能です。

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

v15 以前のプロジェクトでは引き続き middleware.ts が動作します。新規プロジェクトを v16 で始める場合は proxy.ts の命名を採用しましょう。


まとめ

  • Next.js Middleware は SPA でも有効。1 行 で特定パスのリダイレクトを実装できる
  • useEffect の CLS 問題、next.config.js の動的条件不可問題を根本解決できる
  • matcher でパスを絞ることでパフォーマンスへの影響を最小化
  • サブドメイン分岐・Cookie 条件・ヘッダー条件など動的ロジックにも対応可能
  • v16 以降は proxy.ts への移行も視野に

参考リンク

本記事中のコードサンプルは学習目的の最小例です。MIT ライセンス相当として自由にご利用ください。


弊社について

合同会社ジモラボは東京八王子で AI × 業務効率化の SaaS を複数運営する研究所型企業です。AI 議事録 (realingo)・GEO 検索最適化 (lookupai)・AI セキュリティ (Promtect)・会計 (locatax) などを展開しています。


1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?