AWS から Cloudflare に自社サイトを移管した話
先日、コーポレートサイトを Astro ベースでリニューアルしたことをお知らせしました。そのときのインフラは AWS で組んでいましたが、今回あらためて Cloudflare へ移管しました。
このサイトは完全な静的サイトで、サーバー側の処理は問い合わせフォームのメール送信だけです。その構成にしては AWS 側のサービスがやや多い、というのが移管のきっかけでした。本記事では、移管前後の構成、手順、そして実際にハマったポイントを記録として残します。同じような小規模サイトのインフラを見直す方の参考になればと思います。
移管の狙い
- 構成をシンプルにする: 課金ポイントと管理対象のサービスを減らす
- デプロイの自動化: 手動の
aws s3 sync+ CloudFront キャッシュ無効化をやめる - 一元管理: DNS・ホスティング・SSL を1つのサービスにまとめる
移管前の構成(AWS)
静的サイトの配信を CloudFront + S3 で行い、それに付随して以下のサービスを使っていました。
- Route 53: DNS とドメイン登録
- CloudFront: CDN・コンテンツ配信
- S3: 静的ファイルのホスティング
- ACM: SSL 証明書の発行
- API Gateway + Lambda + SES: 問い合わせフォームのメール送信
それぞれは正しく動いていましたが、静的サイト1つに対してサービスが5〜6種類。デプロイも、ビルド成果物を aws s3 sync で同期し、CloudFront のキャッシュを手元から手動で無効化する運用でした。
flowchart LR
visitor(["サイト訪問者"])
subgraph aws ["AWS"]
direction TB
cloudfront["CloudFront(CDN・配信)"]
s3[("S3(静的ファイル)")]
apigw["API Gateway"]
lambda["Lambda"]
ses["SES"]
route53["Route 53(DNS・ドメイン)"]
acm["ACM(SSL証明書)"]
cloudfront --> s3
apigw --> lambda --> ses
end
mail(["運営の受信箱"])
visitor -->|ページ閲覧| cloudfront
visitor -->|フォーム送信| apigw
ses -->|通知メール| mail
route53 -.-> cloudfront
acm -.-> cloudfront
移管後の構成(Cloudflare + Resend)
- Route 53: ドメインレジストラとしてのみ残す(ネームサーバーは Cloudflare に変更)
- Cloudflare: DNS・ホスティング・SSL をまとめて担当
- Cloudflare Workers: サイト本体の配信と問い合わせ API を1つの Worker で兼ねる
- Resend: メール送信
flowchart LR
visitor(["サイト訪問者"])
subgraph cf ["Cloudflare"]
direction TB
dns["DNS / ホスティング / SSL"]
subgraph worker ["Cloudflare Workers(単一)"]
direction TB
assets["Static Assets(dist/ を配信)"]
api["/api/contact ルート"]
end
end
resend["Resend"]
mail(["運営の受信箱"])
route53["Route 53(レジストラのみ)"]
visitor -->|ページ閲覧| assets
visitor -->|フォーム送信| api
api -->|メール送信| resend
resend -->|通知メール| mail
route53 -.-> dns
ポイントは Worker の使い方です。サイトは静的なので、Cloudflare の Workers Static Assets でビルド済みファイル(dist/)をそのまま配信します。一方、問い合わせフォームだけはサーバー側の処理が必要なので、同じ Worker に /api/contact というルートを1つ用意し、そこで Resend を呼んでメールを送ります。
サイトとフォーム API が同一オリジンになるため、CORS の設定が不要になり、環境変数もデプロイも一元管理できます。AWS では別サービスに分かれていたフォームのバックエンドが、サイトと同じ Worker の中に収まりました。
デプロイは Cloudflare の Workers Builds を使い、GitHub と連携しています。main ブランチへ push すると自動でビルドとデプロイが走るため、手動の同期作業はなくなりました。
移管手順
大きく4ステップで進めました。
- DNS を Cloudflare へ移管 — ドメインのネームサーバーを Cloudflare のものに変更し、既存の DNS レコードを移行する。
- サイトを Workers にデプロイ — Workers Builds に GitHub リポジトリを接続し、ビルドコマンドを設定する。
- フォームを Resend に切り替え —
/api/contactルートを実装し、API Gateway + Lambda + SES から置き換える。送信元ドメインを Resend 側で検証する。 - AWS リソースの段階的削除 — 新構成での動作確認が取れてから、CloudFront・S3・Lambda などを順に停止・削除する。
いきなり全部を切り替えず、確認しながら段階的に進めたことで、問題があれば切り戻せる状態を保ちました。
ハマったポイント
正直なところ、全体の設計よりも細部でつまずきました。
ネームサーバー変更の反映待ち
DNS のネームサーバーを Cloudflare に向けても、その変更がインターネット全体に行き渡るまでには時間がかかります。切り替えた直後は、見る環境によって新旧どちらの構成につながるか分かれる状態になります。移管作業はこの反映待ちを見込んで、スケジュールに余裕を持たせるのが無難です。
Resend の Suppression List
テスト送信を繰り返すうちに、特定の宛先にだけメールが届かなくなりました。Resend は、バウンス(宛先不明)や苦情のあったアドレスを Suppression List(送信抑制リスト)に登録し、以降はそのアドレスへの送信を自動的にスキップします。テスト中に存在しないアドレスへ送ってしまい、それがリストに載っていたのが原因でした。ダッシュボードからリストを確認し、解除することで解決しました。
ビルド・デプロイまわりの罠
コードを CI でビルドする段階でも、いくつか足を取られました。
- pnpm のバージョン差: ローカルと Workers Builds とで pnpm のメジャーバージョンが異なり、
pnpm installが失敗しました。package.jsonのpackageManagerフィールドでバージョンを固定して解決しています。 - ビルドスクリプトの承認: pnpm は近年、依存パッケージの postinstall スクリプトをデフォルトでブロックするようです。CI のビルドが対象パッケージで止まったため、設定ファイルで明示的に実行を許可しました。
コスト
分かりやすい例を挙げると、DNS のホストゾーンは Route 53 で月 0.5 ドルほどかかっていましたが、Cloudflare では無料です。1つ1つは小さな額ですが、こうした課金ポイントが複数あり、移管によって整理されました。
金額そのものより、「課金ポイントが減り、コストの見通しが立てやすくなった」ことが実利でした。
パフォーマンス
このサイトはもともと静的かつ軽量に作ってあり、AWS 時代も CloudFront によるエッジ配信でした。そのため移管で劇的に速くなったわけではありませんが、Cloudflare のグローバルなエッジネットワークで配信され、表示速度の指標(PageSpeed Insights)は良好な水準を維持しています。

移管後のトップページの PageSpeed Insights スコア(パフォーマンス 97 / ユーザー補助 96 / おすすめの方法 100 / SEO 100)。LCP 1.0 秒、CLS 0 と Core Web Vitals も良好です。
SSL 証明書も Cloudflare 側で自動的に発行・更新されるため、証明書の管理(ACM)を意識する必要がなくなりました。
まとめ
静的サイト + 問い合わせフォームというシンプルな構成を、それにふさわしいシンプルなインフラに寄せた、というのが今回の移管でした。サービスの数が減り、デプロイは push するだけ、SSL は自動。運用の手数が確実に減りました。
そして、自分たちのサイトで新しい構成を実際に試しておくことには、受託開発の会社として別の意味もあります。お客様に提案する前に、自分たちで使い、ハマりどころまで把握しておく。本記事のような記録は、その過程で得た知見をそのまま残したものです。
当社は業務システム・AI活用・Webサービスの開発から保守までを伴走支援しています。「インフラ構成を見直したい」「運用の手間を減らしたい」といったご相談も承っています。お問い合わせフォーム、または LINE 公式アカウントからお気軽にどうぞ。