CLAUDE LABEN
BILLING — 6/15に告知されていた課金変更(Agent SDK・headless・GitHub Actions・他社エージェントを別枠の月次クレジットへ移す案)は撤回・保留に。これらは引き続きサブスクリプションの上限内で扱われますMANAGED — Code w/ ClaudeでManaged Agentsを発表。サンドボックス内で動作しprivate MCPへ接続でき、実行環境も到達先サービスも企業の管理境界内に収まりますLIMITS — 同会議でClaude Codeのレート上限が倍増、APIの上限も引き上げられました。多段のエージェント運用に余裕が生まれますSUBAGENTS — Claude Codeにネスト型サブエージェント(サブエージェントが別のエージェントを生成)と、壊れた構成を隔離する安全モードが追加されましたEXPORT — Fable 5・Mythos 5は米政府の輸出管理指令による停止が継続中(6/12〜)。Opus・Sonnet・Haikuなど他の全モデルは通常どおり稼働していますCODE — /doctor・Remote Control・/bugの改善やフォールバックモデルの拡張など、Claude Codeの更新が継続していますBILLING — 6/15に告知されていた課金変更(Agent SDK・headless・GitHub Actions・他社エージェントを別枠の月次クレジットへ移す案)は撤回・保留に。これらは引き続きサブスクリプションの上限内で扱われますMANAGED — Code w/ ClaudeでManaged Agentsを発表。サンドボックス内で動作しprivate MCPへ接続でき、実行環境も到達先サービスも企業の管理境界内に収まりますLIMITS — 同会議でClaude Codeのレート上限が倍増、APIの上限も引き上げられました。多段のエージェント運用に余裕が生まれますSUBAGENTS — Claude Codeにネスト型サブエージェント(サブエージェントが別のエージェントを生成)と、壊れた構成を隔離する安全モードが追加されましたEXPORT — Fable 5・Mythos 5は米政府の輸出管理指令による停止が継続中(6/12〜)。Opus・Sonnet・Haikuなど他の全モデルは通常どおり稼働していますCODE — /doctor・Remote Control・/bugの改善やフォールバックモデルの拡張など、Claude Codeの更新が継続しています
記事一覧/Claude Code
Claude Code/2026-06-17中級

デプロイ直後に「500」と出るのに curl は 200 を返す — Next.js のエラー境界が ChunkLoadError を取り違えていた話

本番のNext.js記事ページがブラウザで「500 Internal Server Error」と表示されるのにcurlでは200。原因はサーバーではなくChunkLoadErrorとerror.tsxの誤表示でした。切り分け手順と自動リロードを組み込んだ修正を記録します。

Next.js7Cloudflare Workers12ChunkLoadErrorトラブルシューティング35error boundary

夜中、自分のサイトの記事を開いたら「500 Internal Server Error」。

リロードすると、何事もなかったように表示されます。

数分後、別の記事でまた同じ画面。胸の奥がざわつきました。サーバーが落ちているなら、こんなに気まぐれな出方はしないはずです。

個人開発で、Dolice Labs として4つのNext.jsサイトをCloudflare Workers上で運用しています。デプロイは1日に何度も走ります。この「ときどき500、リロードで回復」という症状を、私自身しばらくキャッシュの問題だと思い込んでいました。結論から言うと、サーバーは一度も500を返していませんでした。

同じ環境で同じ症状に何度も出会ったからこそ、表示と実態のずれにようやく気づけました。再現性の低いエラーほど、思い込みで原因を決めつけてしまいがちです。

まず「本当に500なのか」を疑う

ブラウザの表示を信じる前に、HTTPレスポンスそのものを確認します。ここを飛ばすと、存在しないサーバーエラーを延々と追うことになります。

同じURLに対して、ステータスコードだけを取り出します。

# 表示は「500」だが、実際のステータスは?
curl -s -o /dev/null -w "%{http_code}\n" \
  https://claudelab.net/articles/claude-code/your-article-slug
 
# 念のため10連続で叩いて揺れを見る
for i in $(seq 1 10); do
  curl -s -o /dev/null -w "%{http_code} " \
    https://claudelab.net/articles/claude-code/your-article-slug
done; echo

返ってきたのは、10回すべて 200 でした。

不正な Cookie を付けても、長大な slug を投げても、RSC ヘッダーを付けても、サーバーは一貫して200を返します。つまり「500」はサーバーの応答ではなく、ブラウザの中で起きた何かが描いた絵だったのです。

犯人は ChunkLoadError だった

ブラウザのコンソールを開いて、500画面が出た瞬間のログを残しておきます。そこに ChunkLoadError が並んでいました。

何が起きているか。Next.js は JavaScript をハッシュ付きの細かいチャンク(framework-abc123.js のようなファイル)に分割して配信します。新しいデプロイが走ると、これらのファイル名が変わります。

ところが、ユーザーのブラウザにはまだ古いページが残っています。そのページが「さっきまであったチャンク」を取りに行くと、サーバー側ではもう別名に置き換わっていて404。読み込みに失敗したJavaScriptは例外を投げ、Next.jsのクライアント側エラー境界error.tsx)が発火します。

ここまでで、サーバーは無関係だとわかります。デプロイ頻度が高いサイトほど、この「古いタブが新しいデプロイにぶつかる」瞬間が増えます。1日に何度もデプロイする私の運用では、毎日必ず誰かがこの瞬間を踏んでいた計算になります。

なぜ「500」と表示されたのか

src/app/[locale]/error.tsx を開いて、ようやく腑に落ちました。

// 旧実装(問題あり): あらゆるクライアントエラーを「500」と決めつけていた
'use client';
 
export default function Error({ error, reset }: {
  error: Error & { digest?: string };
  reset: () => void;
}) {
  return (
    <div className="error-page">
      <h1>500 Internal Server Error</h1>
      <p>サーバーでエラーが発生しました</p>
      <button onClick={reset}>再試行</button>
    </div>
  );
}

error.tsx は、ChunkLoadError でもハイドレーション不一致でも、クライアント側のあらゆる例外で表示されます。それなのに見出しに「500 Internal Server Error」と固定文言を書いていたため、サーバーとは無関係のエラーまで「サーバー500」に見えていたのです。

しかも reset() はコンポーネントの再マウントを試みるだけで、すでに失敗したチャンクの読み込みはやり直せません。だから「再試行」を押しても直らず、ユーザーが手でリロードしたときだけ回復していました。

自動リロードを組み込んで誤表示をなくす

修正の方針は3つです。ChunkLoadError の大半は新しいチャンクを取り直せば消えるので、初回エラー時に一度だけ自動リロードします。そして文言から「500」を外し、原因の手がかりをコンソールに残します。

'use client';
 
import { useEffect } from 'react';
 
export default function Error({ error, reset }: {
  error: Error & { digest?: string };
  reset: () => void;
}) {
  useEffect(() => {
    // 原因究明のため、エラー名と digest を必ず残す
    console.error('[error boundary]', error.name, error.message, error.digest);
 
    // ChunkLoadError は古いチャンク参照が原因。一度だけ自動リロードで透過回復
    const isChunkError =
      error.name === 'ChunkLoadError' ||
      /Loading chunk [\d]+ failed/.test(error.message);
 
    if (isChunkError && typeof window !== 'undefined') {
      const KEY = 'cl_chunk_reloaded';
      if (!sessionStorage.getItem(KEY)) {
        sessionStorage.setItem(KEY, '1');
        window.location.reload();
      }
    }
  }, [error]);
 
  return (
    <div className="error-page">
      <h1>読み込みエラーが発生しました</h1>
      <p>ページの読み込みに失敗しました。再読み込みで回復することがあります。</p>
      <button onClick={() => {
        sessionStorage.removeItem('cl_chunk_reloaded');
        window.location.reload();
      }}>
        ページを再読み込み
      </button>
    </div>
  );
}

ポイントは sessionStorage のフラグです。これがないと、リロード後にまた同じエラーが出たとき無限リロードに陥ります。一度自動リロードしたら印を付け、それでも直らなければ静かに手動ボタンへ委ねます。

sessionStorage はプライベートブラウジングで例外を投げることがあるので、本番では try/catch で包んでください。reset() は ChunkLoadError には効かないため、ここでは使っていません。

エッジキャッシュにエラー画面を焼き付けない

もう一つ見落としがちな点があります。Cloudflare のようなエッジでHTMLをキャッシュしている場合、デプロイ遷移の一瞬に生成された壊れた応答を、そのまま固定化してしまう危険です。

私の環境では、キャッシュ層でエラーの痕跡を含むHTMLを保存しないようガードを足しました。具体的には、エラー境界のマーカー(data-error-boundary のような目印)を含むHTML、</html> が欠落したHTML、本文が空の記事HTMLはキャッシュ対象から外します。

これでGooglebotに壊れたページが配信される経路も塞げます。クロール健全性の観点でも、エラー画面の固定化は避けたい挙動でした。Cloudflare運用全体の設計は、Next.jsをCloudflare Workersで運用するときの実務メモに整理しています。

同じ罠を踏まないための確認順序

症状が「ときどき500、リロードで回復」だったら、まず curl でステータスコードを確かめてください。200が返るなら、その時点でサーバーは容疑から外れます。

次にブラウザのコンソールで ChunkLoadError の有無を見ます。あれば、犯人はデプロイとチャンク名の食い違いです。そして error.tsx に固定文言の「500」が残っていないか、自動リロードが入っているかを確認します。

私自身、表示を鵜呑みにして最初の数時間をキャッシュ調査に溶かしました。表示されたエラーコードと、実際のHTTPステータスは別物だと意識するだけで、切り分けは一気に速くなります。

関連して、ネットワーク起因の見分け方はClaude Code のネットワーク接続エラーの切り分けに、Workers のバンドル設計はContent Split Architecture で 62 MiB 制限を回避するにまとめてあります。

同じ「気まぐれな500」に悩んでいる方の、切り分けの一歩目になれば幸いです。

シェア

お読みいただきありがとうございます

Claude Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • コピー&ペーストで使える実装コード付き
  • 毎日新しい上級ガイドを追加
  • ¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

もしこの記事がお役に立ちましたら、チップ(¥150)で応援いただけると大変励みになります。広告なしでの運営を続けるため、皆さまのご支援が大きな力になっています。

関連記事

Claude Code2026-04-21
Claude Code で Next.js × Cloudflare Workers の本番運用を乗り越える — バンドルサイズ危機からエッジキャッシュ設計まで、実際に詰まった場所と解決策を全公開
Next.js アプリを Cloudflare Workers で運用するとき、Claude Code は単なるコード補完ツール以上の存在になります。62 MiB バンドル制限・エッジキャッシュ設計・Content Split Architecture など、実運用で詰まった問題と解決策を完全公開。
Claude Code2026-05-28
Cloudflare Workers の 62 MiB 上限を踏んだ朝に、5,000 記事規模で組み直した Content Split アーキテクチャ
個人で 4 ブログを Cloudflare Workers + Next.js で運用していたある朝、articles.json が肥大化して Worker bundle が 62 MiB 上限を踏み、デプロイが止まりました。メタデータと HTML 本文を分離する Content Split Architecture に組み替えた具体的な実装と、切り替え 1 ヶ月で観測したビルド時間・キャッシュ効果・運用上の落とし穴を共有します。
Claude Code2026-05-22
Claude Code が書き出す MDX のフロントマターで YAML パースエラーが出る原因と対処
Claude Code に MDX 記事を量産させていると、ある日突然 YAML パースエラーで build が落ちます。ダブルクォート内のダブルクォート、引用符を含む日本語、コロンを含むタイトル — 引っかかる原因と、実運用で安定する5つの書き方を整理しました。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →