はじめに
Next.jsのアプリをデプロイする方法を調べた。
選択肢は大きく3つ。Vercel(Next.jsを作った会社のホスティング)、Cloudflare Pages、AWS(ECS/Lambda)。それぞれ特徴が違うので、どれを選ぶべきか整理してからVercelで実際にデプロイした。
3つの選択肢の概要
Vercel:
Next.jsを作ったVercel社のホスティング
ゼロ設定でデプロイできる
GitHubにプッシュするだけでCI/CDが動く
Cloudflare Pages:
エッジネットワークでの配信
無料枠が充実
Workers/R2との連携が自然
AWS(ECS / Lambda):
既存のAWSインフラと統合しやすい
設定の自由度が高い
他のAWSサービスとの連携が必要なとき
Vercelへのデプロイ
手順
# Vercel CLIをインストール
npm install -g vercel
# ログイン
vercel login
# デプロイ(初回)
vercel
# 本番デプロイ
vercel --prod
初回のvercelコマンドで対話形式の設定が走る。GitHubと連携するとプッシュのたびに自動デプロイされる。
vercel.json
{
"buildCommand": "npm run build",
"outputDirectory": ".next",
"installCommand": "npm ci",
"framework": "nextjs",
"regions": ["hnd1"],
"env": {
"NODE_ENV": "production"
},
"headers": [
{
"source": "/(.*)",
"headers": [
{
"key": "X-Content-Type-Options",
"value": "nosniff"
},
{
"key": "X-Frame-Options",
"value": "DENY"
}
]
}
],
"rewrites": [
{
"source": "/api/backend/:path*",
"destination": "https://api.example.com/:path*"
}
]
}
regions: ["hnd1"]で東京リージョンを指定。日本向けのサービスは必ず設定する。
環境変数の設定
# CLIで設定
vercel env add FASTAPI_URL production
vercel env add AUTH_SECRET production
# 一覧確認
vercel env ls
VercelのダッシュボードのSettings → Environment Variablesでも設定できる。
環境の種類:
Production → main/masterブランチ
Preview → PRブランチ(プレビュー環境が自動生成される)
Development → ローカル開発
PRを作るとプレビューURLが自動生成されるのはVercel特有の機能で便利。
Vercelのプレビューデプロイ
main ブランチにPRを作る
↓
Vercelが自動でビルド
↓
プレビューURL(https://my-app-git-feature-xxx.vercel.app)が発行される
↓
レビューアーがプレビューURLで動作確認
↓
マージすると本番に自動デプロイ
PRごとにプレビュー環境が作られる。LaravelをVagrantやDockerで開発していたときは「本番に近い環境での確認」が面倒だったが、Vercelだとブランチを作るだけで確認できる。
Cloudflare Pagesへのデプロイ
# Wranglerをインストール
npm install -g wrangler
# ログイン
wrangler login
# デプロイ
npx wrangler pages deploy .next --project-name my-app
# wrangler.toml
name = "my-app"
compatibility_date = "2024-01-01"
pages_build_output_dir = ".next"
[vars]
NODE_ENV = "production"
Cloudflare PagesのNext.js対応
# @cloudflare/next-on-pagesを使う
npm install -D @cloudflare/next-on-pages
// package.json
{
"scripts": {
"pages:build": "npx @cloudflare/next-on-pages",
"pages:deploy": "wrangler pages deploy .vercel/output/static"
}
}
// next.config.mjs
/** @type {import('next').NextConfig} */
const nextConfig = {
// Cloudflare Pages用の設定
experimental: {
runtime: 'edge',
},
};
export default nextConfig;
Cloudflare PagesはEdge Runtimeで動くため、Node.js固有のAPIが使えない制約がある。fsモジュールや一部のNode.jsライブラリが使えないので、FastAPIへのAPIコールにnode-fetchを使っている場合は確認が必要。
Cloudflare Pagesの制約
✓ 無料枠が充実(月500ビルド、無制限リクエスト)
✓ エッジで動くので世界中で高速
✓ Workers/D1/R2との連携が自然
✗ Node.js APIが使えない(EdgeランタイムのみサポートでNが出ることがある)
✗ Next.jsのすべての機能に対応していない場合がある
✗ 公式サポートではないためVercelより設定が複雑
AWSへのデプロイ
パターン① ECSでDockerコンテナをデプロイ
# Dockerfile
FROM node:20-slim AS base
FROM base AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build
FROM base AS runner
WORKDIR /app
ENV NODE_ENV production
COPY --from=builder /app/public ./public
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
RUN useradd -r nextjs
USER nextjs
EXPOSE 3000
CMD ["node", "server.js"]
# compose.yaml(ローカル確認用)
services:
app:
build: .
ports:
- "3000:3000"
environment:
- FASTAPI_URL=http://backend:8000
// next.config.mjs(standaloneモードを有効化)
const nextConfig = {
output: 'standalone', // Dockerに必要な最小構成を出力
};
output: 'standalone'を設定するとDockerイメージを最小化できる。
パターン② Lambda + API Gatewayでサーバーレス
npm install -D serverless serverless-nextjs-plugin
# serverless.yml
myNextApp:
component: '@sls-next/serverless-component@latest'
inputs:
bucketName: my-next-app-bucket
name:
defaultLambda: my-next-app
domain: example.com
3つのプラットフォームの比較
| 項目 | Vercel | Cloudflare Pages | AWS ECS |
|---|---|---|---|
| 設定の手軽さ | ◎ | ○ | △ |
| プレビュー環境 | 自動生成 | 自動生成 | 自前実装 |
| 無料枠 | 制限あり | 充実 | 従量課金 |
| エッジ配信 | ○ | ◎ | 設定必要 |
| Node.js対応 | ◎ | △(Edge制約) | ◎ |
| 既存インフラ統合 | △ | △ | ◎ |
| カスタムドメイン | 無料 | 無料 | 別途設定 |
| バンドル料金 | 高め | 安め | 使った分 |
Vercelを選ぶ理由・選ばない理由
Vercelを選ぶ:
✓ Next.jsのすべての機能を確実に使いたい
✓ プレビュー環境を手軽に使いたい
✓ デプロイの設定に時間をかけたくない
✓ チームが小さい・個人開発
Vercelを選ばない:
✗ AWSにすでに構築済みのインフラがある
✗ コスト管理をAWSで一元化したい
✗ データを特定リージョンに閉じたい要件がある
✗ 大規模トラフィックでコストが高くなる
実際のCI/CDフロー(GitHub Actions + Vercel)
Vercelの自動デプロイではなく、GitHub ActionsからVercelにデプロイする構成。テストを挟みたいときに使う。
# .github/workflows/deploy.yml
name: Deploy to Vercel
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run type check
run: npm run type-check
- name: Run lint
run: npm run lint
- name: Run tests
run: npm test
deploy-preview:
needs: test
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
steps:
- uses: actions/checkout@v4
- name: Deploy to Vercel (Preview)
uses: amondnet/vercel-action@v25
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
deploy-production:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- name: Deploy to Vercel (Production)
uses: amondnet/vercel-action@v25
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
vercel-args: '--prod'
カスタムドメインの設定
# Vercel CLIでドメインを追加
vercel domains add example.com
# DNSの確認
vercel domains inspect example.com
DNSの設定(ドメインレジストラ側):
Aレコード:
@ → 76.76.21.21
CNAMEレコード:
www → cname.vercel-dns.com
Cloudflareでドメインを管理している場合は、CloudflareのProxyを経由するかどうかを選べる。VercelのSSL証明書とCloudflareのProxyが競合する場合はDNS-onlyにする。
パフォーマンス最適化
画像最適化
import Image from 'next/image';
export default function Hero() {
return (
<Image
src="/hero.jpg"
alt="ヒーロー画像"
width={1200}
height={600}
priority // LCPに影響する画像はpriority
placeholder="blur" // ぼかしのプレースホルダー
/>
);
}
フォントの最適化
// app/layout.tsx
import { Noto_Sans_JP } from 'next/font/google';
const notoSansJP = Noto_Sans_JP({
subsets: ['latin'],
weight: ['400', '500', '700'],
display: 'swap',
variable: '--font-noto-sans-jp',
});
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="ja" className={notoSansJP.variable}>
<body className="font-sans">{children}</body>
</html>
);
}
next/fontを使うとGoogleフォントがビルド時にダウンロードされてセルフホストされる。外部フォントのリクエストが不要になってパフォーマンスが上がる。
まとめ
- Vercelはゼロ設定でNext.jsをデプロイできる。PRごとのプレビュー環境が便利
- Cloudflare PagesはEdge配信と無料枠が充実。Node.js APIの制約に注意
- AWSはECSでDockerコンテナをデプロイする方法が既存インフラとの統合に向いている
- 個人開発・小規模チームはVercelが一番手軽
- 既存のAWSインフラがある場合はECSが自然な選択
PHPのデプロイ(LaravelをサーバーにSSHしてgit pull + composer install)と比べると、Vercelのデプロイは「GitHubにプッシュするだけ」で別次元に楽だった。プレビュー環境の自動生成はチームレビューを大きく変える機能で、一度使うと戻れない。