tech10分で読める

Next.js マルチドメイン & ホスティング運用まとめ

Next.js 13 App Routerでマルチドメインサイトを構築・運用するための包括的ガイド。SSR、ISR、SSGの使い分けから実装時の注意点まで実践的に解説します。

#nextjs#multi-domain#app-router#hosting#ssg#isr#middleware#マルチドメイン

Next.js マルチドメイン & ホスティング運用まとめ

本記事の構成: 1つのNext.jsアプリで複数ドメイン(personal、company、blog)を運用する実践的なガイドです。実装から運用まで包括的に解説しています。

📊 レンダリング方式の理解と現在のトレンド

レンダリング方式比較表

方式生成タイミング表示速度SEOサーバー負荷適用シーン
SSGビルド時のみ⚡⚡⚡ 最高速🥇 優秀ゼロ固定コンテンツ
ISRビルド時+定期更新⚡⚡ 高速🥇 優秀更新のあるコンテンツ
SSRリクエストごと⚡ 高速🥇 優秀リアルタイム必須
SPAクライアント側🐌 低速🥉 困難ゼロ管理画面・アプリ

🎯 現在のトレンド:ISRがスタンダードに

結論: Next.js界隈ではISRが実質スタンダードになりつつあります。ただし、ユースケースによってSSGも全然現役です。

📅 歴史的変遷

  • 2016-2019 (v1-v8): SSRが中心、getInitialPropsの時代
  • 2019-2020 (v9-v10): SSG登場、getStaticPropsで静的サイト革命
  • 2020-2022 (v9.5-v12): ISR登場、「静的+動的」のハイブリッド時代
  • 2022-現在 (v13+): App Router、Server Componentsで新時代

🏗️ 実用的な組み合わせ戦略

サイト特性別の推奨方式

サイト特性推奨方式理由実装のコツ
企業サイトSSG固定コンテンツ中心revalidateなし
個人ポートフォリオSSG更新頻度が低い同上
ブログ・ニュースISR定期的な記事更新revalidate: 3600
ECサイトISR在庫・価格変動revalidate: 300
ダッシュボードSSRリアルタイム必須Server Components

💡 実践的なパターン

✅ 推奨構成 ├── 固定ページ(About、Contact、LP) → SSG ├── 更新ページ(Blog、News、Products) → ISR └── 動的ページ(Admin、Dashboard) → SSR

🌐 マルチドメインの実現方法

🎯 基本戦略

  • 1つのNext.jsアプリで複数ドメインを運用
  • Middlewareによるrewriteでホストベースのルーティング
  • URLはそのまま、内容のみ動的に切り替え
  • SEO最適化とパフォーマンスを両立

ディレクトリ構成例(App Router対応)

src/ middleware.ts ← srcディレクトリ内に配置 app/ personal/ ← アンダースコアなし company/ ← プライベートフォルダ扱いを回避 blog/ page.tsx ← Welcomeページ

middleware例(App Router対応版)

import { NextResponse } from 'next/server'; import type { NextRequest } from 'next/server'; export function middleware(req: NextRequest) { const url = req.nextUrl; const host = req.headers.get('x-forwarded-host') || req.headers.get('host') || url.hostname; // ポート番号を除去 const hostWithoutPort = host.split(':')[0]; const isPersonal = hostWithoutPort === 'rkinoshita.com' || hostWithoutPort === 'personal.localhost' || hostWithoutPort.startsWith('personal.'); const isCompany = hostWithoutPort === 'rk-sys.com' || hostWithoutPort === 'company.localhost' || hostWithoutPort.startsWith('company.'); const isBlog = hostWithoutPort === 'blog.rk-step.com' || hostWithoutPort === 'blog.localhost' || hostWithoutPort.startsWith('blog.'); // 重要: App Routerでは_付きフォルダは使用不可 if (isBlog && !url.pathname.startsWith('/blog')) { return NextResponse.rewrite(new URL(`/blog${url.pathname}${url.search}`, url)); } if (isPersonal && !url.pathname.startsWith('/personal')) { return NextResponse.rewrite(new URL(`/personal${url.pathname}${url.search}`, url)); } if (isCompany && !url.pathname.startsWith('/company')) { return NextResponse.rewrite(new URL(`/company${url.pathname}${url.search}`, url)); } return NextResponse.next(); } export const config = { matcher: [ '/((?!api|_next/static|_next/image|favicon.ico|robots.txt|sitemap.xml|assets/).*)', ], };

重要な変更点:

  • ポート番号を除去: host.split(':')[0]
  • App Router対応: /_personal/personal
  • 開発環境対応: personal.localhostを追加

🔄 リダイレクト vs リライト

項目RedirectRewrite
URLバー変わる変わらない
実際の動作ブラウザが再アクセスサーバー内部で差し替え
SEO正規URLを明示表のURLそのまま
用途URL正規化や恒久移動マルチドメイン切り替え

📦 完全静的エクスポート(Static Export)

概要

Next.jsアプリケーションを完全な静的サイトとして出力し、任意のWebサーバーでホスティング可能にする機能です。

コマンド

next build && next export

出力例

out/ index.html about/index.html contact/index.html

制約事項

  • getServerSideProps / Middleware は利用不可
  • App Routerは制約が多い → 完全静的化はPages Router推奨
  • 画像最適化を使用する場合はnext.config.jsに以下を追加:
module.exports = { images: { unoptimized: true } }

適用シーン

  • CDN配信での最高速度が必要
  • サーバーレス環境での運用コスト削減
  • GitHub PagesやNetlifyなどの静的ホスティング利用

🔒 SEO・セキュリティ観点での考慮事項

SEO観点でのレンダリング方式比較

レンダリング初期表示速度クローラビリティメタタグ制御推奨用途SEO評価
SSR⚡ 高速✅ 優秀✅ 動的ニュース、EC🥇 最高
ISR⚡ 高速✅ 優秀✅ 動的ブログ、CMS🥇 最高
SSG⚡⚡ 最高速✅ 優秀⚠️ 静的コーポレート🥇 最高
SPA🐌 低速❌ 困難⚠️ 制限管理画面🥉 低い

マルチドメインSEO戦略

✅ 推奨アプローチ

  • サブドメイン分離: blog.example.com, shop.example.com
  • 専用sitemap.xml: 各ドメインごとに個別生成
  • canonical URL: 重複コンテンツ防止
  • robots.txt: ドメインごとの適切な設定

⚠️ 注意すべき点

  • 重複コンテンツ: 同一コンテンツの複数ドメイン配信は避ける
  • 内部リンク: ドメイン間リンクは外部リンク扱い
  • ドメインオーソリティ: 分散による権威性の希薄化

セキュリティ観点での実装考慮事項

CSP (Content Security Policy)

// next.config.js での設定例 const nextConfig = { async headers() { return [ { source: '/(.*)', headers: [ { key: 'Content-Security-Policy', value: "default-src 'self'; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline';" } ] } ] } }

CORS設定

// middleware.ts でのドメイン制御例 export function middleware(request: NextRequest) { const response = NextResponse.next() // 許可されたドメインからのリクエストのみ処理 const allowedOrigins = ['rkinoshita.com', 'rk-sys.com', 'blog.rk-step.com'] const origin = request.headers.get('origin') if (origin && allowedOrigins.includes(origin)) { response.headers.set('Access-Control-Allow-Origin', origin) } return response }

セキュリティヘッダー設定

  • X-Frame-Options: Clickjacking攻撃防止
  • X-Content-Type-Options: MIME型スニッフィング防止
  • Referrer-Policy: リファラー情報制御
  • Permissions-Policy: ブラウザ機能アクセス制限

📊 マルチドメイン対応一覧表

レンダリング × ホスティング × ルーター対応表

レンダリングVercelCloudflare PagesAWS S3+CloudFrontRouterリダイレクト/リライトSEOセキュリティ
SSR✅ 完全対応✅ Edge対応❌ 不可App/PagesRewrite推奨🥇 最高🔒 高
ISR✅ 完全対応⚠️ 制限あり❌ 不可App/PagesRewrite推奨🥇 最高🔒 高
SSG✅ 完全対応✅ 完全対応✅ 完全対応App/PagesRewrite/Redirect両方🥇 最高🔒🔒 最高
SPA✅ 完全対応✅ 完全対応✅ 完全対応App/PagesRedirect推奨🥉 低い⚠️ 注意
Static Export✅ 対応✅ 対応✅ 最適Pages推奨CloudFront Functions🥈 良い🔒🔒 最高

マルチドメイン実装方法別対応表

実装方法VercelCloudflare PagesAWS S3+CloudFront設定場所複雑度セキュリティリスク
Middleware Rewrite✅ ネイティブ✅ Pages Functions❌ 不可middleware.ts🔒 低リスク
Next.js Rewrites✅ 対応✅ 対応❌ 不可next.config.js🔒 低リスク
サブディレクトリ✅ 対応✅ 対応✅ 対応各プラットフォーム⚠️ 要注意
Lambda@Edge❌ 不要❌ 不要✅ 必須CloudFront🔒 設定次第

決定フローチャート

マルチドメインサイトを構築したい ↓ [要件を確認] ↓ ┌─ 動的コンテンツが必要? ─┐ ↓ YES ↓ NO [SSR/ISR] [SSG/Static Export] ↓ ↓ 予算は? ホスティング重視? ↓ ↓ 安い → Cloudflare Pages 速度 → Vercel/CloudFlare 高い → Vercel 制御 → AWS S3+CloudFront ↓ ↓ Pages Functions Lambda@Edge or でrewrite ビルド時ディレクトリ分岐

🏢 ホスティングサービス比較

セキュリティ・SEO機能比較

機能VercelCloudflare PagesAWS S3+CloudFront
SSL/TLS証明書🔒 自動🔒 自動🔒 ACM連携
DDoS保護✅ 標準✅✅ 強力✅ AWS Shield
WAF❌ 無し✅ 標準✅ AWS WAF
カスタムヘッダー✅ 対応✅ 対応✅ 対応
IPアクセス制限💰 有料✅ 無料✅ 対応
GDPR対応✅ 対応✅ 対応✅ 対応
サイトマップ生成✅ Next.js✅ Next.js⚠️ 手動
Analytics✅ 標準✅ 標準⚠️ 別途設定

セキュリティベストプラクティス

1. 環境変数の管理

// 本番環境でのみ重要な情報を含める const isProduction = process.env.NODE_ENV === 'production' const apiUrl = isProduction ? process.env.PROD_API_URL : process.env.DEV_API_URL

2. ログインページの保護

// middleware.ts でのアクセス制限 import { NextRequest, NextResponse } from 'next/server' export function middleware(request: NextRequest) { const url = request.nextUrl // 管理画面へのアクセス制御 if (url.pathname.startsWith('/admin')) { const authCookie = request.cookies.get('auth-token') if (!authCookie) { return NextResponse.redirect(new URL('/login', request.url)) } } }

3. レート制限の実装

// API Routesでのレート制限例 const rateLimitMap = new Map() export default function handler(req, res) { const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress const now = Date.now() const windowMs = 15 * 60 * 1000 // 15分 const maxRequests = 100 const requests = rateLimitMap.get(ip) || [] const windowStart = now - windowMs const requestsInWindow = requests.filter(time => time > windowStart) if (requestsInWindow.length >= maxRequests) { return res.status(429).json({ error: 'Too many requests' }) } requestsInWindow.push(now) rateLimitMap.set(ip, requestsInWindow) // 実際のAPI処理 }

🔍 各サービスの詳細比較

Vercel

Next.js開発元による最適化されたプラットフォーム

  • 対応機能: SSR/ISR/middleware/画像最適化すべて完全対応
  • マルチドメイン: ダッシュボードUIで簡単に複数ドメイン割り当て可能
  • メリット: セットアップが最短、開発体験が最高
  • デメリット: ベンダーロックイン懸念、コストが高め

Cloudflare Pages

エッジ配信とコスト効率に優れる

  • 対応機能: @cloudflare/next-on-pages経由でNext.js Edge機能対応
  • マルチドメイン: Pages Functionsでrewrite実装可能
  • メリット: コスト効率が良い、Cloudflareエコシステムとの統合性
  • デメリット: 一部Next.js機能に制限あり

AWS S3 + CloudFront

大規模運用向けのフルマネージド構成

  • 対応機能: 完全静的エクスポート前提
  • マルチドメイン: CloudFront Functions/Lambda@EdgeでHost判定→サブディレクトリrewrite
  • メリット: 高い可用性、細かい制御が可能
  • デメリット: MiddlewareやISR不可、セットアップが複雑

⚠️ 実装時の重要な注意点

App Routerでのディレクトリ命名規則

❌ よくある間違い

src/app/_personal/ ← アンダースコア付き src/app/_company/ ← プライベートフォルダ扱いでルーティング対象外 src/app/_blog/

✅ 正しい実装

src/app/personal/ ← 通常のルートディレクトリ src/app/company/ ← middlewareでアクセス可能 src/app/blog/

理由: Next.js 13のApp Routerでは、_で始まるフォルダはプライベートフォルダとして扱われ、ルーティングから除外される。

middlewareファイルの配置場所

❌ よくある間違い

project/ ├── middleware.ts ← srcディレクトリの外 └── src/ └── app/

✅ 正しい配置

project/ └── src/ ├── middleware.ts ← srcディレクトリの中 └── app/

理由: srcディレクトリを使用している場合、middlewareもsrc内に配置する必要がある。

Turbopackとmiddlewareの相性問題

発生する問題

npm run dev --turbopack # middlewareが実行されない場合がある

解決策

npm run dev # 通常モードで実行

🛠️ よくあるエラーと解決策

ビルド関連

  • sh: next: not foundnpm install実行後にnpx next buildを使用

  • Non-standard NODE_ENVNODE_ENVdevelopment/production/testのみ使用可能。ステージング環境の識別は別の環境変数で対応

型エラー関連

  • Type error: Property 'title' does not exist → コンポーネントのprops型を明確に定義して渡す

App Router移行関連

  • Error: <Html> should not be imported outside of pages/_document → App Routerでは<html>/<head>を素のHTMLタグとして使用。next/documentは使用不可

レンダリング関連

  • Hydration mismatch → サーバーサイドとクライアントサイドで異なる内容がレンダリングされている。useEffectやブラウザ固有のAPIの使用タイミングを見直す

マルチドメイン実装関連

  • middlewareが実行されない → 1) middleware.tssrcディレクトリ内に配置 2) Turbopackを無効化して通常モードで実行

  • 404エラー(ページが見つからない) → App Routerで_付きフォルダは使用不可。src/app/_personal/src/app/personal/に変更

  • ホスト判定が失敗する → ポート番号を除去: host.split(':')[0]でホスト名のみ取得


🎯 推奨実装戦略

段階的移行アプローチ

  1. 現在: Vercelで開発・運用

    • middlewareを活用して3サイトを1リポジトリに統合
    • 各サイトに適したレンダリング方式を選択(SSG/SSR/ISR)
  2. 将来の選択肢: 運用要件に応じて選択

    • コスト重視: Cloudflare Pagesへの移行を検討
    • 大規模運用: AWS S3 + CloudFrontでの静的配信
    • 完全ポータブル: next exportによる完全静的化

🎯 実用的なサイト構成別推奨戦略

あなたのマルチドメイン構成での最適解

ドメイン推奨方式理由更新頻度実装ポイント
Personal (rkinoshita.com)SSGほぼ固定コンテンツrevalidateなしでビルド時生成
Company (rk-sys.com)SSG企業情報は安定同上、SEO最適化重視
Blog (blog.rk-step.com)ISR記事更新が発生頻繁revalidate: 3600(1時間)

ISRの実装例(ブログ向け)

// app/blog/page.tsx (App Router) export const revalidate = 3600 // 1時間ごとに再生成 export default async function BlogPage() { const posts = await getBlogPosts() // CMSや外部APIから取得 return <BlogList posts={posts} /> } // または Pages Router export async function getStaticProps() { const posts = await getBlogPosts() return { props: { posts }, revalidate: 3600 // 1時間 } }

SSGの実装例(企業・個人サイト向け)

// app/about/page.tsx (App Router) // revalidateの指定なし = SSG export default function AboutPage() { return <AboutSection /> } // または静的データのfetch async function getCompanyInfo() { // ビルド時のみ実行される return await fetch('api/company-info') }

現実的な運用での考慮点

✅ ISRのメリット(ブログに最適)

  • 新記事公開後、自動的に一覧ページも更新
  • ユーザーは常にSSG並みの速度を体験
  • CMS更新 → サイト反映のタイムラグが最小限

✅ SSGのメリット(企業・個人サイトに最適)

  • 絶対的な高速表示
  • CDN配信コストが最安
  • サーバー負荷ゼロ

⚠️ 運用での注意点

  • ISRは初回アクセス時に再生成される = 一瞬重い場合がある
  • revalidate時間の設定が重要(短すぎるとサーバー負荷、長すぎると更新反映遅延)
  • App Routerではrevalidate指定なし = 自動でSSG扱い

📖 クイックリファレンス

🚀 最速セットアップ(初心者向け)

プラットフォーム: Vercel レンダリング: SSG ルーター: App Router マルチドメイン: Middleware Rewrite

💰 コスト重視

プラットフォーム: Cloudflare Pages レンダリング: SSG または Static Export ルーター: App Router マルチドメイン: Pages Functions

🏗️ エンタープライズ

プラットフォーム: AWS S3 + CloudFront レンダリング: Static Export ルーター: Pages Router マルチドメイン: Lambda@Edge + サブディレクトリ

⚡ パフォーマンス最優先

プラットフォーム: 任意(CDN配信) レンダリング: SSG ルーター: App Router マルチドメイン: ビルド時生成 + Rewrite

🔒 セキュリティ重視

プラットフォーム: AWS S3 + CloudFront + WAF レンダリング: Static Export ルーター: Pages Router セキュリティ: Lambda@Edge + IPアクセス制限

🎯 SEO最優先

プラットフォーム: Vercel (Analytics付き) レンダリング: SSR または ISR ルーター: App Router SEO対策: 自動sitemap + メタタグ最適化

📈 2025年現在の推奨構成(トレンド反映)

プラットフォーム: Vercel 固定ページ: SSG(企業・個人サイト) 動的ページ: ISR(ブログ・ニュース) ルーター: App Router マルチドメイン: Middleware Rewrite

🔮 Next.js レンダリング方式の変遷と今後

📅 歴史的変遷

  • 2016-2019 (v1-v8): SSRが中心、getInitialPropsの時代
  • 2019-2020 (v9-v10): SSG登場、getStaticPropsで静的サイト革命
  • 2020-2022 (v9.5-v12): ISR登場、「静的+動的」のハイブリッド時代
  • 2022-現在 (v13+): App Router、Server Componentsで新時代

🎯 現在の主流パターン

  1. 大多数のサイト: ISRをベースに、固定部分はSSG
  2. 完全静的サイト: SSG + next export
  3. リアルタイム必須: SSR(管理画面、ダッシュボード)

🔮 今後の予測

  • Server Componentsが更に普及 → SSR/ISRの境界が曖昧に
  • Edge Computingの進化 → より細かい地域でのキャッシュ制御
  • AI/CMS統合が進み → ISRの自動最適化

💡 実践的な選択指針

質問: あなたのサイトは? ├─ 頻繁に更新される? │ ├─ Yes → ISR(revalidate設定) │ └─ No → SSG(最高速&最安) └─ リアルタイムが必須? ├─ Yes → SSR └─ No → ISR or SSG

🎯 まとめ

本記事では、Next.js 13 App Routerでマルチドメインサイトを構築・運用するための包括的な手法を解説しました。

🔑 キーポイント

  1. レンダリング方式: ISRが現在のスタンダード、固定コンテンツはSSGが最適
  2. マルチドメイン実装: Middlewareによるrewriteで1アプリ複数ドメイン運用
  3. 実装注意点: _付きフォルダは使用不可、middlewareはsrc内に配置
  4. ホスティング選択: 要件に応じてVercel/Cloudflare Pages/AWS S3を使い分け

🚀 実装の成功要因

  • 段階的アプローチ: 小さく始めて徐々に機能拡張
  • パフォーマンス重視: 適切なレンダリング方式の選択
  • SEO対策: 各ドメインに最適化されたメタデータ設定
  • 保守性: 明確なディレクトリ構成と責務分離

このガイドを参考に、効率的で保守しやすいマルチドメインサイトを構築してください!

RK

1997年生まれ

ITエンジニア

インフラ・SRE