自社サイトを Vercel から Cloudflare Workers へフル移行した記録
こんにちは!システムデザインワークスの山岸です。
現在ご覧いただいている弊社のコーポレートサイト(Next.js + Payload CMS 構成)のホスティングを、Vercel から Cloudflare Workers へ全面的に移行しました。データベース接続・画像配信・メール送信・定期実行まで含めた「フル移行」です。狙いは、AI 時代に急速に高まるセキュリティリスクへの備えと、コストの最適化でした。
結論から言うと、サイトはいま Cloudflare のエッジ(世界中に分散したサーバー網)上で安定稼働しています。ただ、移行は一筋縄ではいきませんでした。Workers は一般的なサーバーとは仕組みが異なる実行環境のため、「これまで普通に動いていた画像処理やメール送信が、移行した途端に動かなくなる」という壁に何度もぶつかったのです。本記事では、採用した構成と、特にハマった 3 つのポイントを共有します。
背景・課題:AI 時代のセキュリティリスクに、先回りして備えたい
移行前の構成は、ホスティングが Vercel、メディアストレージが Vercel Blob、データベースが Supabase(PostgreSQL)でした。Vercel の開発体験は素晴らしく、不満があったわけではありません。特に、GitHub にプッシュするだけで確認用のプレビュー URL が自動発行される体験は秀逸で、レビューや動作確認のスピードを大きく支えてくれていました。
それでも移行を決めた一番の理由は、セキュリティです。
生成 AI の登場により、攻撃の様相は急速に変わっています。脆弱性の探索や攻撃コードの作成が自動化・高速化され、情報漏洩や脆弱性を突いたハッキングのリスクは、企業規模を問わず急激に高まっています。「うちは小さい会社だから狙われない」が通用しない時代になりました。
Cloudflare は世界中のインターネットトラフィックの相当な割合を中継する、世界最大級のセキュリティ企業です。DDoS 攻撃(大量アクセスでサービスを停止させる攻撃)の防御や WAF(Web アプリケーションファイアウォール)をエッジで標準的に提供しており、アプリケーションが動く場所そのものを Cloudflare 上に置けば、この防御網の内側でサイトを運用できます。
もう一つの理由はコストです。Workers の有料プラン(Workers Paid)は月 5 ドルからと安価で、R2(Cloudflare のオブジェクトストレージ)は転送量課金がありません。セキュリティを高めながら固定費も抑えられる、という点が決め手になりました。
ただし条件を一つ決めていました。Vercel で気に入っていた「プッシュすればプレビュー URL が出る」開発体験は、移行後も手放さないこと。セキュリティとコストのために開発スピードを犠牲にしては本末転倒だからです。
加えて、「まず自分たちで試して、効果と落とし穴を把握してからお客様に提案する」が弊社のスタイルです。今回の移行はその実践でもありました。
移行後の構成:すべて Cloudflare のマネージドサービスに寄せる
Next.js を Cloudflare Workers で動かすために、公式が推奨する OpenNext(@opennextjs/cloudflare)を採用しました。Next.js のビルド成果物を Workers のランタイムである workerd 向けに変換してくれるアダプタです。
役割ごとの構成は次のとおりです。
ホスティング: Cloudflare Workers(OpenNext 経由)
データベース接続: Supabase PostgreSQL へ Hyperdrive(Cloudflare のコネクションプーリングサービス)経由で接続
メディアストレージ: Vercel Blob から R2 へ全件移行
画像リサイズ: sharp(画像処理ライブラリ)の代わりに Cloudflare Image Transformations へ委譲
SSG/ISR キャッシュ: ページキャッシュは R2、タグ管理と再生成キューは Durable Objects(Workers 用の永続ステートフルオブジェクト)
メール送信: SMTP から Cloudflare Email Service へ切替
定期実行: Vercel Cron から Workers Cron Triggers へ移植
デプロイ: GitHub Actions から自動デプロイ(ブランチごとのプレビュー URL 付き)
ポイントは「Workers で動かないものを無理に動かす」のではなく、動かない部分を Cloudflare のマネージドサービスに置き換える方針で統一したことです。
実装・検証の要点:workerd で本当にハマった 3 つ
移行作業の大半は順調でしたが、workerd は Node.js と似て非なるランタイムです。特に苦労した 3 つを紹介します。
1. 間欠的に発生する謎のハング(Error 1101)
移行直後、管理画面など DB アクセスの多いページで、リクエストが応答しないまま失敗する現象(Cloudflare の Error 1101)が間欠的に発生しました。再現条件が安定せず、ローカルでは再現しない厄介なバグです。
原因は 2 つ重なっていました。
Hyperdrive の接続先が IPv6 専用だった: Supabase の direct 接続は IPv6 専用で、これがハングの一因に。IPv4 で到達できる Session pooler(
pooler.supabase.com:5432)へ変更して解消しました。なお Transaction pooler(6543 番ポート)は prepared statement(SQL の事前コンパイル機能)に非対応のため使えませんコネクションプールが workerd の I/O 分離に違反していた: Node.js の感覚でモジュールスコープに pg のコネクションプールを置くと、DB 接続(ソケット)がリクエストをまたいで再利用されます。workerd はリクエスト間の I/O を分離する設計のため、これが後続リクエストの無限ハングを引き起こしていました。Workers 上では接続を使い捨て(
maxUses: 1)にし、実際のプーリングは Hyperdrive に委ねる構成で解決しました
1// Workers 実行時のみ: 接続は使い捨て、プーリングは Hyperdrive に任せる2const pool = new Pool({3 connectionString,4 max: 10,5 maxUses: 1, // リクエストをまたいだソケット再利用を防ぐ6})
修正後は管理画面への同時 20 リクエストがすべて正常応答し、Error 1101 はゼロになりました。
2. sharp が動かない、そして SVG が全滅した
Payload CMS や next/image が使う画像処理ライブラリ sharp はネイティブバイナリを含むため、workerd では起動時にクラッシュします。そこで画像のリサイズ・再エンコードは Cloudflare Image Transformations(/cdn-cgi/image/ の URL に変換を委譲する仕組み)へ切り替えました。
ところが切り替え後、サイト中のロゴや技術アイコンが一斉に表示されなくなりました。原因は、Image Transformations がベクター画像(SVG)の変換に対応しておらず 404 を返すこと。カスタムローダー側で SVG と GIF は変換を通さず原本を直接配信するよう除外して解決しました。
3. 問い合わせメールが「静かに」届かなくなっていた
移行後しばらくして、お問い合わせフォームの通知メールが届いていないことに気づきました。workerd では DNS/TCP の制約により nodemailer の SMTP 送信が機能しません。しかも送信エラーはフォームライブラリ側で握りつぶされており、表面上は正常に見えていました。
対応として、Cloudflare Email Service(send_email バインディング)を使う Payload 用のメールアダプタを実装し、Workers 上では env.EMAIL.send() で送信する構成に切り替えました。ローカル開発では従来どおり SMTP を使い分けています。
エラーが画面に出ない「サイレント障害」は、移行プロジェクトで最も怖い類のものです。移行後は主要動線(問い合わせ・ログイン・記事更新)を実際に通すスモークテストが欠かせないと再認識しました。
やめたこと・採らなかった選択肢
OpenNext 生成物への直接パッチ: 定期実行(cron)を生成された Worker に差し込む案は、OpenNext の更新ごとに壊れるため不採用。cron 専用の小さな Worker を別に立て、service binding(Worker 間の内部呼び出し)で本体 API を叩く構成にしました
CDN キャッシュの積極パージ: 設定不備時に黙って失敗する(silent failure)リスクがあるため、初期構成では意図的に無効化。安定稼働を確認してから段階導入する方針です
結果:セキュリティもコストも狙いどおり、デプロイ体験まで向上
サイト全体が Cloudflare の防御網(DDoS 対策・WAF)の内側で動くようになり、当初の狙いだったセキュリティ面の土台が整った
ホスティングの固定費は Workers Paid(月 5 ドル〜) が基本となり、メディア配信も R2 直配信で転送量課金なし。コストの見通しが立てやすくなった
管理画面・公開ページとも安定稼働し、間欠ハング(Error 1101)は解消
Payload で記事を更新すると該当ページだけが再生成される ISR(ページの差分再生成)も Workers 上で機能
移行の条件にしていた「プッシュすればプレビュー URL が出る」体験も再現。GitHub Actions と Workers のプレビュー機能(preview alias)を組み合わせ、main へのプッシュは本番デプロイ、それ以外のブランチはブランチ名入りのプレビュー URL が自動発行される開発フローを構築できた
なお、バンドルサイズが Workers 無料プランの上限(gzip 3MiB)を超えるため Workers Paid プランが前提になりますが、それでも月 5 ドルからという価格は移行の後押しになりました。
最後に、移行を終えて振り返った両者の比較を表にまとめます。どちらが優れているという話ではなく、Vercel の手軽さを取るか、Cloudflare のセキュリティとコストを取るかという選択だと感じています。
比較項目 | Vercel | Cloudflare Workers |
|---|---|---|
月額基本料金 | Pro プラン: $20/人 | Workers Paid: $5〜 |
メディア配信の転送量 | 転送量に応じて課金 | R2 は転送量課金なし |
セキュリティ | WAF はプラン・設定しだい | DDoS 防御・WAF を標準提供 |
実行環境 | Node.js がそのまま動く | workerd(Node.js 互換は部分的。sharp などのネイティブモジュールは不可) |
画像リサイズ | 組み込みの画像最適化 | Image Transformations へ委譲(SVG / GIF は素通しが必要) |
メール送信(SMTP) | 利用可 | SMTP 不可(Email Service で代替) |
プレビュー URL | プッシュするだけで自動発行 | CI(GitHub Actions)で構築して再現可能 |
構築・移行の手間 | ほぼ設定不要 | OpenNext の導入など個別対応が必要 |
※ 2026 年 6 月時点。Next.js + Payload CMS 構成での弊社の移行経験に基づきます。
まとめ:移行から得た 3 つの学び
workerd は「ほぼ Node.js」ではない: ネイティブモジュール(sharp)、SMTP、リクエストをまたぐソケット再利用——Node.js の常識が通用しない箇所は事前にリストアップしておく
動かないものはマネージドサービスに置き換える: Hyperdrive・R2・Durable Objects・Email Service と、足りないピースは Cloudflare 側に揃っている。無理に互換レイヤーを自作しない
切り戻せる形で段階移行する: メディア移行は「コピーのみ」で旧環境を残し、画像変換には無効化スイッチを用意。安心して前に進むには退路の設計が効く
ご相談お待ちしております。
Web サイト・Web システムのセキュリティ強化や、Vercel・AWS からの移行、Cloudflare Workers での新規構築をご検討中の方、栃木県でシステム開発・アプリ開発の会社をお探しの方は、ぜひ一度ご相談ください。
システムデザインワークスでは、今回のような自社での実践に基づき、インフラ選定から移行・運用まで一貫してサポートしております。お気軽にお問い合わせフォームよりご連絡ください。