2026.03.18

Azure Static Web Apps に Next.js サイトをデプロイした話

Next.jsAzureGitHub Actionsデプロイ

はじめに

個人のナレッジベース兼ブログサイトを Next.js(App Router)で構築し、Azure Static Web Apps にデプロイした。この記事では、ホスティング先の選定理由から実際のデプロイ手順、そしてハマったポイントまでをまとめる。

なぜ Azure Static Web Apps を選んだのか

静的サイトのホスティング先としては Vercel、Netlify、Cloudflare Pages なども候補に挙がったが、最終的に Azure Static Web Apps を選んだ理由は以下の通り。

  • Free プランで十分な機能がある — カスタムドメイン、SSL 証明書の自動管理、ステージング環境が無料枠に含まれる
  • GitHub Actions との統合がシームレス — リポジトリを接続すると CI/CD ワークフローが自動生成され、PR ごとにステージング環境がデプロイされる
  • グローバル分散 — Azure の CDN によりコンテンツが世界中のエッジにキャッシュされる
  • 業務で Azure を使っている — 個人の学習にもなるため、普段使っているクラウドに寄せた

一方で、Vercel のような Next.js 特化の機能(ISR、Middleware、Edge Functions)は使えない。今回のサイトは完全な静的エクスポート(SSG)で問題ないため、この制約は許容範囲だった。

前提条件

  • Next.js 15 以降(App Router)
  • pnpm をパッケージマネージャーとして使用
  • GitHub リポジトリにソースコードがホストされている
  • Azure アカウントを持っている

Next.js 側の設定

静的エクスポートの有効化

Azure Static Web Apps は SSR に対応していないため、next.config.tsoutput: "export" を設定する必要がある。

// next.config.ts
import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  output: "export",
};

export default nextConfig;

これにより next build 実行時に out/ ディレクトリに静的ファイルが出力される。

動的ルートへの対応

App Router で動的ルート([slug][...slug])を使っている場合、各ページに generateStaticParams() を実装する必要がある。これがないとビルド時にエラーになる。

// src/app/blog/[slug]/page.tsx
export function generateStaticParams() {
  return getAllBlogPosts().map((post) => ({ slug: post.slug }));
}

API Routes の除外

開発時のみ使用する API Routes(src/app/api/)や管理画面(src/app/admin/)が存在する場合、静的エクスポート時にこれらがあるとビルドが失敗する。ビルドスクリプトで一時的に退避する方法で対処した。

// scripts/build.mjs(抜粋)
const DEV_ONLY_DIRS = ["admin", "api"];

function moveOut() {
  for (const dir of DEV_ONLY_DIRS) {
    const src = path.join(APP_DIR, dir);
    const dest = path.join(TEMP_DIR, dir);
    if (fs.existsSync(src)) {
      fs.renameSync(src, dest);
    }
  }
}

try {
  moveOut();
  execSync("npx next build", { stdio: "inherit" });
} finally {
  moveBack();  // ビルド後に必ず復元
}

package.jsonbuild コマンドでこのスクリプトを呼び出す。

{
  "scripts": {
    "build": "node scripts/build.mjs"
  }
}

Azure Static Web Apps のセットアップ

1. Azure Portal でリソースを作成

Azure Portal から「Static Web Apps」を検索し、リソースを作成する。

  • プラン: Free
  • ソース: GitHub
  • リポジトリ: 対象のリポジトリを選択
  • ブランチ: main
  • ビルドプリセット: Custom
  • App location: /
  • Output location: out

2. GitHub Actions ワークフローの確認

リソース作成時に、GitHub リポジトリに .github/workflows/ 配下にワークフローファイルが自動的にコミットされる。内容を確認し、ビルドコマンドが正しいことを確かめる。

# .github/workflows/azure-static-web-apps-xxxxx.yml(抜粋)
- name: Build And Deploy
  uses: Azure/static-web-apps-deploy@v1
  with:
    app_location: "/"
    output_location: "out"
    app_build_command: "pnpm build"

ポイントとして、app_build_command にカスタムビルドコマンドを指定できる。デフォルトでは npm run build が実行されるため、pnpm を使っている場合は明示的に変更が必要。

3. カスタムドメインの設定

Azure Portal の Static Web Apps リソースから「カスタムドメイン」メニューで設定する。

  1. DNS レコードに CNAME を追加(サブドメインの場合)
  2. Azure 側でドメインの検証が走る
  3. SSL 証明書が自動で発行・管理される

ハマったポイント

output: "export" と開発サーバーの両立

output: "export" を常時有効にすると、開発時に API Routes が使えなくなる。環境変数で切り替える方法を採用した。

// next.config.ts
const isDev = process.env.NODE_ENV === "development";

const nextConfig: NextConfig = {
  output: isDev ? undefined : "export",
};

これにより pnpm dev では通常モード、pnpm build では静的エクスポートモードで動作する。

画像の最適化

output: "export" では next/image のデフォルト最適化(サーバーサイド処理)が使えない。unoptimized: true を設定するか、外部の画像最適化サービスを利用する必要がある。

全文検索の対応

静的サイトではサーバーサイドの検索 API が使えないため、Pagefind を導入した。ビルド時に静的な検索インデックスを生成し、クライアントサイドで検索を実行する仕組みになっている。

npx pagefind --site out --output-path public/pagefind

まとめ

Azure Static Web Apps は、静的サイトを手軽にデプロイできる選択肢として十分に実用的だった。Next.js の output: "export" との組み合わせでいくつかハマりポイントはあるが、一度設定してしまえば GitHub への push だけでデプロイが完了する快適な開発体験が得られる。

SSR や ISR が必要なケースでは Vercel の方が適しているが、静的サイトで十分なプロジェクトであれば、Azure Static Web Apps の Free プランは有力な候補になるだろう。