エラーの概要

Vercel の 503 エラーは、Vercel のサーバーが一時的に要求を処理できない状態を示します。このエラーは、Vercel のインフラストラクチャー側の問題またはデプロイされたアプリケーションのリソース制限に起因することが多く、ユーザーのアクセスが一時的にブロックされます。通常は数分から数時間で自動的に復旧しますが、原因の特定と対応は重要です。

実際のエラーメッセージ例

{
  "statusCode": 503,
  "message": "Service Unavailable",
  "error": "The server is temporarily unable to handle the request."
}
503 Service Unavailable

ブラウザでアクセスすると、Vercel の標準エラーページが表示されるか、上記のようなレスポンスが返却されます。

よくある原因と解決手順

原因1:Vercel のインフラストラクチャー側の障害

Vercel のグローバルネットワークやデータセンター側で一時的なサービス中断が発生している場合、全ユーザーのデプロイが 503 エラーを返します。これはユーザーのコード変更とは関係なく発生します。

確認方法: Vercel の公式ステータスページを確認することで、グローバルな障害の有無を即座に判定できます。

Before(エラーが起きるコード):

# エラーの原因が不明なまま再試行を繰り返す
curl https://<your-domain>.vercel.app
# 503 Service Unavailable が返却される

After(修正後):

# 1. Vercel ステータスページで障害を確認
# https://vercel.statuspage.io にアクセス

# 2. 障害がない場合は数分待機してから再試行
curl https://<your-domain>.vercel.app

# 3. 障害情報がある場合は、復旧まで待機
# ステータスページの Twitter 公式アカウント(@vercelstatus)でも通知される

この場合、ユーザー側でできることは限定的です。Vercel チームが復旧作業を進めている間、数分~数時間の待機が必要になります。

原因2:サーバーレス関数の同時実行数が上限に達した

Vercel のサーバーレス関数(Next.js API Routes、Edge Functions など)には、プランごとに同時実行数の上限が設定されています。大量のアクセスや長時間の処理を実行する関数が複数起動すると、すぐに上限に達し、新規リクエストが 503 で拒否されます。

Before(エラーが起きるコード):

// pages/api/slow-process.js
export default async function handler(req, res) {
  // 10秒のブロッキング処理
  await new Promise(resolve => setTimeout(resolve, 10000));
  res.status(200).json({ message: 'Done' });
}

このエンドポイントに 100 人が同時アクセスすると、10 秒以内に古い処理が完了しないため、同時実行数上限に達して 503 が発生します。

After(修正後):

// pages/api/fast-process.js
export default async function handler(req, res) {
  // 処理を複数のステップに分割
  // 1. 即座にレスポンスを返す
  // 2. バックグラウンドでの処理はキューに移譲
  
  if (req.method === 'POST') {
    const jobId = await queue.add(req.body);
    res.status(202).json({ jobId, message: 'Processing started' });
    return;
  }
  
  res.status(405).json({ error: 'Method not allowed' });
}

// Bull(ジョブキュー)または Vercel KV を使用した非同期処理
import { Queue } from 'bullmq';
const queue = new Queue('long-tasks', {
  connection: { host: '<your-redis-host>', port: 6379 }
});

queue.process(async (job) => {
  // 重い処理をここで実行
  return { result: 'completed' };
});

同時実行数が頻繁に超過する場合は、Vercel のプランを上位に変更することも検討してください。

原因3:デプロイ直後のコールドスタートが重なった場合

新規デプロイ後、アプリケーションがまだ完全に初期化されていない状態で大量のトラフィックが流入すると、複数の関数インスタンスが同時に起動しようとして 503 が発生することがあります。

Before(エラーが起きるコード):

// lib/db.js
import { connectDatabase } from '@mydb/client';

let connection = null;

export async function getConnection() {
  if (!connection) {
    // コールドスタートのたびに3秒かかる
    connection = await connectDatabase({
      host: process.env.DB_HOST,
      timeout: 3000
    });
  }
  return connection;
}

After(修正後):

// lib/db.js
import { connectDatabase } from '@mydb/client';

let connection = null;
let isInitialized = false;

export async function initializeConnection() {
  if (isInitialized) return connection;
  
  connection = await connectDatabase({
    host: process.env.DB_HOST,
    timeout: 3000,
    // コネクションプーリングを活用
    poolSize: 5
  });
  
  isInitialized = true;
  return connection;
}

// pages/api/health.js - デプロイ後のウォーミングエンドポイント
export default async function handler(req, res) {
  const conn = await initializeConnection();
  res.status(200).json({ status: 'ready' });
}

デプロイ後、ウォーミングスクリプトで https://<your-domain>.vercel.app/api/health を複数回呼び出すことで、インスタンスを事前に起動させておきます。

ツール固有の注意点

Vercel Dashboard で実行数を監視する

Vercel Dashboard の「Usage」セクションで、関数の同時実行数と実行時間を確認できます。

# Vercel CLI で現在のプラン情報を確認
vercel list
vercel inspect <deployment-url>

エッジミドルウェアとサーバーレス関数の使い分け

エッジミドルウェアは認証やリダイレクトなど軽量な処理に向いており、同時実行数制限がより高く設定されています。長時間の処理はサーバーレス関数で実行し、キューイングシステムを組み合わせるべきです。

// middleware.js - エッジで軽量処理を実行
import { NextResponse } from 'next/server';

export function middleware(request) {
  // リクエストの検証は高速
  if (!request.headers.get('authorization')) {
    return NextResponse.redirect(new URL('/login', request.url));
  }
  
  return NextResponse.next();
}

export const config = { matcher: ['/api/protected/:path*'] };

Vercel KV を使ったレート制限

キャッシュとレート制限を組み合わせることで、サーバーレス関数の過負荷を防げます。

// pages/api/limited.js
import { kv } from '@vercel/kv';

export default async function handler(req, res) {
  const ip = req.headers['x-forwarded-for'];
  const key = `rate:${ip}`;
  const count = await kv.incr(key);
  
  if (count === 1) {
    await kv.expire(key, 60); // 1分間のウィンドウ
  }
  
  if (count > 100) {
    return res.status(429).json({ error: 'Too many requests' });
  }
  
  res.status(200).json({ remaining: 100 - count });
}

それでも解決しない場合

1. Vercel チームへ問い合わせ

  • Vercel Dashboard の「Support」からサポートチケットを作成してください。
  • 障害情報やデプロイ ID を記載することで、診断がスムーズになります。

2. ログを確認する

# Vercel CLI で最新のログを表示
vercel logs <your-domain>.vercel.app --follow

# 特定のデプロイのビルドログを確認
vercel logs <deployment-id>

3. 公式ドキュメントとコミュニティ


免責事項:本記事の内容は、執筆時点の公開情報をもとに作成したものです。ソフトウェアの仕様は予告なく変更されることがあります。最新の情報は各ツールの公式サポートページをご確認ください。本記事の情報を利用した結果生じたいかなる損害についても、著者および運営者は責任を負いかねます。