
【トラブルシューティング】Cloudflare Pages → Workers 移行で遭遇したEdge Runtime問題集
[toc]
はじめに
-
microCMSからNotion APIへの移行に伴い、デプロイ基盤もCloudflare PagesからWorkersに刷新した
-
@cloudflare/next-on-pagesのEdge Runtime制約では Notion APIクライアント等のNode.js依存パッケージが動かない
-
最終的にOpenNext(@opennextjs/cloudflare)で解決したが、移行過程で多くの問題に遭遇した
-
この記事では、各問題の原因と解決策を時系列で記録する
1. crypto モジュール問題
症状
Error: Module not found: Can't resolve 'crypto'
@notionhq/client(Notion公式SDK)がNode.jsのcryptoモジュールに依存しており、Edge Runtimeでビルドエラーが発生。
原因
Cloudflare Pages(Edge Runtime)ではNode.jsのビルトインモジュール(crypto, fs, path等)が使用できない。@notionhq/clientは内部でリクエスト署名にcryptoを使用している。
解決策: Notion REST APIを直接fetchで呼び出し
SDKを使わず、Notion REST APIを直接fetchで呼び出す方式に変更した。
// libs/notion.ts — SDK不使用版
const NOTION_API_BASE = 'https://api.notion.com/v1';
const headers = {
'Authorization': `Bearer ${process.env.NOTION_TOKEN}`,
'Notion-Version': '2022-06-28',
'Content-Type': 'application/json',
};
export async function queryDatabase(databaseId: string, filter?: any) {
const response = await fetch(`${NOTION_API_BASE}/databases/${databaseId}/query`, {
method: 'POST',
headers,
body: JSON.stringify({ filter }),
});
return response.json();
}
2. zenn-markdown-html の crypto依存
症状
Module not found: Can't resolve 'crypto'
at node_modules/zenn-markdown-html/...
ブログのMarkdownレンダリングに使用していたzenn-markdown-htmlがNode.jsのcryptoに依存していた。
原因
zenn-markdown-htmlは内部でMarkdownのハッシュ生成やKaTeX処理にcryptoモジュールを使用している。
解決策: markdown-itに置換
zenn-markdown-htmlをmarkdown-itに置き換えた。markdown-itはブラウザ互換で、Edge Runtimeでも動作する。
// Before
import markdownToHtml from 'zenn-markdown-html';
const html = markdownToHtml(body);
// After
import MarkdownIt from 'markdown-it';
const md = new MarkdownIt({
html: true,
highlight: (str, lang) => {
// highlight.jsでシンタックスハイライト
},
});
const html = md.render(body);
3. cheerio の Node.js依存
症状
Module not found: Can't resolve 'stream'
at node_modules/cheerio/...
目次生成やリンクカード作成に使用していたcheerioがNode.jsのstreamモジュールに依存していた。
原因
cheerioはhtmlparser2をベースにしており、Node.jsのstreamモジュールに依存。Edge Runtimeでは利用不可。
解決策: 正規表現ベースのHTML操作に書き換え
// Before: cheerioでの目次生成
import cheerio from 'cheerio';
const $ = cheerio.load(html);
const headings = $('h2, h3').map((_, el) => ({
id: $(el).attr('id'),
text: $(el).text(),
level: parseInt(el.tagName[1]),
})).get();
// After: 正規表現での目次生成
function extractHeadings(html: string) {
const regex = /<h([23])[^>]*id="([^"]*)">([^<]*)<\/h[23]>/g;
const headings = [];
let match;
while ((match = regex.exec(html)) !== null) {
headings.push({
level: parseInt(match[1]),
id: match[2],
text: match[3],
});
}
return headings;
}
4. highlight.js は実はEdge互換
当初の認識(間違い)
highlight.jsもEdge Runtimeで動かないと思い込んでいた。
実際の原因
highlight.js自体はEdge Runtime互換。問題だったのはcheerio経由で使っていたこと。cheerioがNode.js依存だったため、巻き添えでエラーになっていた。
解決策: markdown-itのhighlightオプションで直接使用
import hljs from 'highlight.js';
import MarkdownIt from 'markdown-it';
const md = new MarkdownIt({
html: true,
highlight: (str: string, lang: string) => {
if (lang && hljs.getLanguage(lang)) {
try {
return `<pre class="hljs"><code>${hljs.highlight(str, { language: lang }).value}</code></pre>`;
} catch (__) {}
}
return `<pre class="hljs"><code>${md.utils.escapeHtml(str)}</code></pre>`;
},
});
cheerioを排除したことで、highlight.jsは問題なく動作するようになった。
5. Workers CPU制限 Error 1102
症状
Error 1102: Worker exceeded CPU time limit
デプロイ後、すべてのページで1102エラーが返される。
原因
Cloudflare Workers無料プランのCPU時間制限は10ms。Next.jsのSSRレンダリングは通常50ms〜200msのCPU時間を必要とするため、無料プランでは実行できない。
解決策: $5/月の有料プランにアップグレード
# Cloudflareダッシュボード → Workers & Pages → Plans
# Workers Paid ($5/month) にアップグレード
有料プランではCPU時間が30秒まで拡張され、Next.jsのSSRが問題なく動作する。
6. 503エラー大量発生
症状
有料プランにアップグレード後も、全ページで503エラーが返される。Workerログには以下のエラー:
Error: Environment variable NOTION_TOKEN is not set
原因
Worker Secretsが未設定だった。GitHub Actionsのsecretsはビルド時のみ有効で、ランタイムのWorkerには渡されない。Workerが実行時に参照する環境変数はWorker Secretsとして別途設定する必要がある。
解決策: wrangler secret putで環境変数をWorkerに直接設定
# Worker Secretsに必要な環境変数を設定
echo "ntn_xxxxxxxxxxxxx" | npx wrangler secret put NOTION_TOKEN
echo "2eca0ffb-xxxx-xxxx-xxxx-xxxxxxxxxxxx" | npx wrangler secret put NOTION_DATABASE_ID
echo "G-XXXXXXXXXX" | npx wrangler secret put NEXT_PUBLIC_GA_ID
echo "https://kt-tech.blog" | npx wrangler secret put SITE_URL
wrangler.toml vs Worker Secrets
| 設定方法 | 用途 | セキュリティ |
|---|---|---|
| wrangler.toml [vars] | 公開して良い設定値 | Gitにコミットされる |
| wrangler secret put | APIキー等のシークレット | 暗号化して保存 |
| GitHub Actions secrets | ビルド時の環境変数 | ビルド時のみ有効 |
Tips
npx edge-runtime --eval "require('package-name')"でテストできるwrangler devでは再現しない。必ず本番環境でテストすることwrangler tailでリアルタイムログを確認。環境変数の未設定が原因であることが多いまとめ
-
Cloudflare PagesのEdge Runtime制約はNode.js依存パッケージを多用するプロジェクトには厳しい
-
crypto依存は@notionhq/clientだけでなく、zenn-markdown-html等の間接依存にも潜んでいる
-
cheerioの代替は正規表現、zenn-markdown-htmlの代替はmarkdown-itで対応可能
-
highlight.jsはEdge互換。cheerioとの組み合わせが問題だっただけ
-
Workers無料プランのCPU 10ms制限はNext.jsには不足。$5/月の有料プランが必須
-
Worker Secretsの設定忘れは503エラーの最も多い原因。ビルド時の環境変数とランタイムの環境変数は別物
最新記事
- 【設定・環境構築】OpenNext でNext.js SSGサイトをCloudflare Workersにデプロイする完全ガイド
2026/3/19
- 【実装】Notion calloutブロックをNext.jsでカラフルなUIコンポーネントとして表示する
2026/3/19
- 【トラブルシューティング】Cloudflare Pages → Workers 移行で遭遇したEdge Runtime問題集
2026/3/19
- 【実践】Next.js 13→16メジャーアップグレードの全記録 — 破壊的変更と対応策
2026/3/19
- 【自動化】Gemini Imagen APIでブログのeyecatch画像を自動生成してR2にアップロードする
2026/3/19
- 【実装】Notion APIでブログシステムを構築する(Next.js 13 App Router × SDK v5)
2026/3/19
- 【移行ガイド】microCMSからNotion APIへブログCMSを完全移行する
2026/3/19
- 【トラブルシューティング】本番デプロイで遭遇した問題と解決策まとめ
2026/3/15
- 【環境構築】Next.js × Cloudflare Workers の本番環境を一から構築する
2026/3/15
- 【設定・環境構築】Neon → Prisma Postgres 移行とローカル開発環境の構築
2026/2/26


