前回バックエンドをWorkersに置いたので今度はフロント側。素のHTMLで書いていたサイトをAstroで組み直して、ついでにサイト本体の置き場所もCloudflareへ移した。独自ドメインをそのまま引き継ごうとしたところで思わぬ制約に当たり、結果的にDNSまで引っ越すことになったので、その経緯も含めて書いておく。
2026-06-15静的サイトにバックエンドを追加する素のHTMLをGitHub Pagesに置いた静的サイトに、ランニングコストをかけずCloudflare Workersでバックエンドを足した記録。Honoの書き味と、お問い合わせフォームをTurnstile・Resend・KVでAPI化するまで。Astroで作り直す
素のHTMLのイケてないところは、ヘッダーやフッターを直すたびに全ページを同じように書き換えないといけないこと。ページが2枚しかなくても地味に面倒だし、増やす予定があるならなおさら。
Astroは静的サイト向けのフレームワークで、ページを部品(コンポーネント)に分けて書いておくと、ビルド時にただのHTMLへ組み上げてくれる。ブラウザに送られるJavaScriptは基本ゼロ。動きが必要な部分だけを島のように切り出してJSを載せる、という考え方(アイランドアーキテクチャ)になっているそうだ。
Next.jsも考えたけど、うちのサイトは2ページとモーダルくらいの素朴な静的コンテンツで、Reactのランタイムを常に積む理由がない。書き味もAstroのほうがHTMLの延長に近い。まずは、GitHub Pagesのままコンポーネント化だけ行った。
実際の組み方はこんな感じで、セクションごとに部品を切り出して、ページ側は並べるだけにしてある。
BaseLayout
├─ SiteHeader
├─ HeroSection
│ └─ HeroMap(島原半島の地図、将来なにかに使いたいと思ってる)
├─ AboutSection
│ └─ SocialLinks
├─ ServicesSection
│ ├─ ServiceCard
│ └─ ServiceModal
├─ ContactSection
└─ SiteFooter
└─ SocialLinks
ヘッダーを1行直せば全ページに反映されるので、ページが増えるほど効いてくる。
繰り返し出てくる部分はデータにまとめた。サービス紹介のカードと、クリックで開く詳細モーダル。この両方をservices.tsというファイル1つから組み立てるようにしたので、文言を直したいときは画面を作るコードに触らなくてよくなった。
前記事でも書いたかもだが、ローカルでの開発環境はdocker composeで用意している。ホストにNode.jsのバージョンや依存を持ち込みたくないし、他人が書いたライブラリを大量に落としてくるので、コンテナに閉じ込めておいたほうが安心という理由もある。git cloneしてdocker compose upすれば動く状態にはしてある。
ここで1つハマったのがベースイメージ選び。軽さに惹かれてnode:22-alpineにしたら動かなかった。Alpineは多くのLinuxが使うglibcではなく、muslという別のCライブラリで動いている。そのため、glibc前提で配られているものはそのまま動かない。Cloudflareのランタイムがこれに当たるし、ビルドに使うRollupも中身はネイティブバイナリなので、musl用の別パッケージが要る。素直にnode:22-slimにしたら解決した。
サイト本体もCloudflare Pagesへ
ここまでで、サイト本体はGitHub Pages、バックエンドはCloudflare Workers、ドメインとDNSはAWS Route 53、という3社に分かれた状態になっていた。ふだん触らない場所ほど、しばらく経つと「あの設定どこだっけ」となる。せっかくバックエンドをCloudflareに置いたので、サイト本体もそっちに寄せることにした。
Cloudflare PagesにはGit連携ビルドがある。GitHubのリポジトリと繋いでおくと、pushのたびにCloudflare側がビルドして配信してくれるので、デプロイ用のワークフローを自前で持たなくてよくなる。しかもブランチごとに開発版のURLを作ってくれる。Vercelとかでもできるけど、この機能地味にありがたくて、他の人にも本番合流前に確認してもらえるし、ステージング環境とか作らなくていいからいいよね。
移行自体は、Pagesにデプロイできる状態にしてから、Route 53のドメインをそっちに向けるだけ。そう思って軽く構えていた。
Apexドメインで詰む
最初はDNSをRoute 53に残したまま、Cloudflare Pagesにドメインだけ向けるつもりだった。ところがこれがうまくいかなかったのだ。
Apexドメインの制約と、うまくいく構成
wwwなしのloopsketch.comのようなアドレスをapexドメインと呼ぶ。ここには転送設定(CNAME)を置けない決まりになっている。apexには、このドメインをどのDNSサーバが管理しているかを示すレコードが必ず必要で、CNAMEは仕様上ほかのレコードと同じ名前に同居できないため。
じゃあIPアドレスを直接書けばいいかというと、Cloudflare Pagesは外部のDNSに書けるような固定のIPを公開していない。apexをPagesに向ける正式な方法は、DNSごとCloudflareの管理下に置いて、CNAMEフラット化(apexのCNAMEを応答時にIPへ変換する仕組み)に任せることだけ。Route 53のALIASも考えたが、設定できるのはAWS自身のサービスだけだった。
wwwありのサブドメインならCNAME一本で向けられる。でもこのサイトは昔からwwwなしを正式なアドレスにしてきたので、そこは変えたくない。
ということで急遽DNSごと引っ越すことになった。もともと一元化が目的だったし、DNSだけAWSに残すのは目的とも噛み合っていない。手順としては、Route 53の全レコードを書き出して、Cloudflareにドメインを追加して(既存の設定は自動で読み取ってくれるが、取りこぼしがないか突き合わせる)、レジストラでネームサーバを変更する、という流れ。DNSにはメール送信の認証(SPF / DKIM / DMARC)も入っていて、ここを移し忘れるとメールが届かなくなる。ビクビクしながらやったけど、大きな障害は発生せず移行できた。ふぅ~
まとめ
やったこと自体は、書き方を変えて置き場所を移すだけの地味な作業。それでもapexドメインの制約で一度つまずいたので、地味な作業ほど落とし穴があるなと思う。
終わってみると、フロントもバックエンドもドメイン管理もCloudflareにまとまったので便利になったかも。ダッシュボードを開けばアクセス数もキャッシュ率も一望できるし、正直、見ていて楽しい。散らかった机を片付けたら思ったより気分が良かった、という感じに近い。
LOOPSKETCHでは、こうした構成でのサイト構築や既存サイトの刷新も請けています。一人でやってるので運用をお受けするのは難しいですが、Cloudflareに移設して身軽になりたい、みたいなことだけでしたらサポートできます。よろしければ、メニューのお問い合わせから、ぜひご連絡ください。