有料レンタルサーバーからVercel+Cloudflareに移行したら、爆速&料金0円になった話
はじめに
このサイト「すくまりのメモ帳」は2020年を最後に更新が止まっていた、いわゆる塩漬けサイトでした。ただ、Unity・Egret Engine系の技術記事や自作HTML5ゲームは今でも普通にアクセスされていて、捨てるにはもったいない資産でした。
一方で、WordPress+有料レンタルサーバーという構成は、更新していなくてもサーバー代とDBの運用コストだけはずっとかかり続けます。今回、記事とゲームは残したまま、土台をまるごとNext.jsの静的サイトに作り直し、VercelとCloudflareの無料枠に移行しました。結果として、サーバー代は0円になり、表示速度も体感でかなり速くなりました。この記事ではその移行のやり方と、実際に何を得て何を手放したかをまとめます。
移行前の構成と課題
移行前は WordPress + Cocoonテーマを有料レンタルサーバー上で動かし、記事本文やタグはMySQLに保存していました。ページを開くたびにPHPがDBへクエリを投げて、HTMLをその場で組み立てて返す構成です。
このタイプの構成は、更新をまったくしていなくても
- サーバーを常時起動しておく費用
- MySQLのバックアップ・脆弱性対応などDB運用の手間
が発生し続けます。塩漬けサイトにとってはランニングコストばかりが重く、見合わなくなっていました。
移行の進め方:レンタルサーバーの中身をClaude Codeに解析させる
移行作業は開発環境にClaude Code(Sonnetモデル)を使って進めました。
最初にやったのは、レンタルサーバー上のpublic_html一式(WordPress本体・アップロード画像・DBバックアップZIPなど)をまるごとローカルにダウンロードすることでした。
そのフォルダをClaude Codeに読み込ませて、DBダンプから記事・カテゴリ・タグ・固定ページを全部抽出させ、現状のサイト構造を洗い出したサイトマップを作らせました。これによって「今何が何本あって、どのURLに何が生えているか」をClaude Codeに覚えさせた状態を作り、そこから
- どの記事・ページを移行するか/しないか(非公開記事や下書き、広告関連のプラグイン設定など)
- カテゴリだったものをどうタグに再設計するか
- 自作ゲームのフォルダ名・URLをどう整理するか
といった移行後のURL構造・情報設計を、洗い出したサイトマップを土台にひとつずつ詰めていきました。いきなり移行スクリプトを書き始めるのではなく、先に「現状の正確な棚卸し」を作ってから設計に入ったのが、大きな手戻りを避けられたポイントだったと思います。
技術選定
移行後の技術スタックは、Next.js・Tailwind CSS・Vercel・Cloudflare R2という組み合わせにしました。
- Next.js:
output: exportで完全な静的サイト生成ができ、MDXでの記事執筆ともそのまま相性が良いため採用しました - Tailwind CSS: コンポーネント単位のCSSファイルは作らず、ユーティリティクラスだけでスタイリングを完結させています。配色はTailwindの
@themeトークンとして一元管理し、コンポーネント側で生の16進数カラーを直接書かないようにしました - Vercel: Next.jsを開発している会社自身が提供するホスティングサービスで、設定なしでNext.jsの静的サイトをそのままデプロイできます。GitHubへのpushだけで自動的にビルド・デプロイされる手軽さも決め手でした
- Cloudflare R2: 記事画像の保存先。S3互換のオブジェクトストレージですが、R2最大の特徴は転送量(エグレス)に課金が発生しない点です。画像を大量に配信するブログにとって、この部分のコストがかからないのは大きなメリットでした
移行後の構成
移行後はNext.jsで全記事を事前にビルド時に静的HTML化し(output: export)、Vercelの無料枠にデプロイする構成にしました。
- 記事本文の画像はCloudflare R2に置き、間に薄いプロキシとしてCloudflare Workerを挟んで配信。R2バケットはそのままではHTTP公開されない仕組みなので、Workerに
R2バケットバインディングを紐付けてURLパスをオブジェクトキーとしてbucket.get(key)し、writeHttpMetadata()でヘッダーを組み立てて返す、というやり方にした。これは独自実装ではなく、Cloudflare公式ドキュメントのUse R2 from Workersに載っている定番パターンそのまま - 自作HTML5ゲームはビルド済みの静的ファイル一式をそのままリポジトリに同梱し、Vercelが直接返す(画像用のR2を経由させる必要がない)
- 問い合わせページはWordPress側の専用フォームをやめ、Googleフォームに置き換えて埋め込むだけにした
という形で、サーバーを自分で常時起動しておく必要がある部分がなくなりました。
Vercelを使うのは今回が初めてでしたが、UIの設定で迷ったところはAIに聞きながら進めれば特に難しくなく、GitHubにpushするだけで自動的にビルド・デプロイまでやってくれるのは素直に便利でした。
なぜSSG・タグ構成にしたか
移行後の構成を選ぶ際、いくつか判断のポイントがありました。
- SSGを選んだ理由: 今後増やしていく予定の記事数を踏まえても、タグや日付による絞り込みはクライアントサイドの静的ページだけで十分にまかなえる規模だったため、サーバー側でレンダリングする仕組みは不要と判断し、完全SSG構成にしました
- カテゴリではなくタグにした理由: カテゴリは1記事に複数付けるのが難しいのに対し、タグなら複数付けられ、後からタグをまとめてカテゴリ的にグルーピングすることもできます。分類の柔軟性を優先してタグ運用に切り替えました
- タグと年度の複合絞り込みをやめた理由: 旧サイトのGA4の解析データを確認したところ、絞り込み機能自体がほとんど使われていませんでした。そのため、タグ×年度のように条件を組み合わせた絞り込みは実装せず、タグ・年度それぞれ単独の軸だけの静的ページにとどめています
なぜ爆速になったか
移行前は「アクセスが来るたびにPHPがDBに問い合わせてHTMLを組み立てる」動的な仕組みだったのに対し、移行後は「あらかじめビルド時に生成しておいた静的HTMLを、CDNがそのまま返すだけ」という仕組みです。リクエストのたびにサーバー側で計算する処理がほぼ無くなったぶん、素直に速くなりました。
同じ記事ページでPageSpeed Insights(デスクトップ)を計測した結果がこちらです。
| 指標 | 旧サイト | 新サイト |
|---|---|---|
| パフォーマンススコア | 67 | 100 |
| First Contentful Paint | 0.8秒 | 0.3秒 |
| Largest Contentful Paint | 1.4秒 | 0.7秒 |
| Total Blocking Time | 510ミリ秒 | 10ミリ秒 |
| Cumulative Layout Shift | 0 | 0 |
| Speed Index | 2.7秒 | 0.4秒 |
旧サイトの計測結果はこちらです。
新サイトの計測結果はこちらです。
実はこの数値、最初に計測したときはパフォーマンススコアが76点で、LCPだけ4.2秒と旧サイトより悪化していました。
PageSpeed Insightsの指摘を見ると記事のヒーロー画像が最適化されずに重いまま配信されていたのが原因で、画像最適化パイプラインを直したところ、LCPが4.2秒→0.9秒、パフォーマンススコアも76点→98点まで改善しました。
移行して構成を変えただけで自動的に速くなったわけではなく、画像のような重い要素はちゃんと最適化しないと逆に遅くなる、というのも実際にやってみて分かったことです。この画像最適化、WordPress時代はプラグインが裏側で自動的にやってくれていたことに、自分の手でパイプラインを組んで初めて気づきました。静的サイトにするというのは、こうした「今まで誰かが肩代わりしてくれていた処理」を自分たちの手に引き取ることでもあるようです。
なぜ無料になったか
旧サイトが使っていたのはXserverのスタンダードプランで、年間更新の請求予定金額は13,200円(月額換算で約1,100円)でした。これが、VercelとCloudflareの無料枠だけで完全に0円になりました。記事数・アクセス量ともに個人ブログの規模であれば、静的サイト配信も画像配信も無料枠の範囲に収まります。DBを持たなくなったこと自体も、運用コストを下げている要因のひとつです。
容量面でもかなり余裕がありました。画像の保存先にしたCloudflare R2の無料枠は10GBですが、旧サイトから実際に移行した画像・ゲームアセットの合計はおよそ155MBほど。無料枠の1.5%程度しか使っておらず、当分は容量を気にする必要がなさそうです。
問い合わせフォームでも同じような判断をしています。WordPress時代はスパム対策をプラグインが肩代わりしてくれていましたが、静的サイトで自前フォームを実装するとなるとスパム対策も含めて一から作り込む必要があり、開発コストが見合いません。そのためGoogleフォームに置き換えて埋め込むだけにし、実装コストをかけずに済ませました。
運用コストの面でもう一つ意識したのが、Claude Codeへの依存です。記事追加・画像アップロード・リンク切れチェックなどの作業は、画像の最適化(WebP変換)からCloudflare R2へのアップロードまで含めてすべてNode.jsのスクリプトとしてtools/配下に用意し、作業者はコマンドを1つ叩くだけで完結するようにしました。加えて、それぞれの手順を人力でも追える運用手順書としてDocsにまとめてあります。これは、Claude Codeという特定のAIツールへの依存やコストを、いざとなれば切り離せるようにするための保険です。スクリプト自体は普通のNode.jsで書いてあるので、Claude Codeを解約しても、手元のマシンでコマンドを叩けば記事の検証や画像アップロードは今まで通り動きます。
開発中に直面した問題:責務の肥大化と曖昧な命名
Claude Codeに任せて進めるのは基本的に快適でしたが、途中で設計が崩れていく問題にも直面しました。ページのコンポーネント(page.tsx)にデータ取得もUI表示も区別なくどんどん処理が書き足されていき、変数名もresolveのような、何をしているのか名前からは分からないものが増えていったのです。
このままではまずいと感じ、開発の途中で
- データ取得・ロジックを持つ「コンテナ」と、表示専用の「プレゼンテーショナル」コンポーネントに分ける
- 意味の分からない汎用的な命名を、責務が分かる名前に付け替える
というリファクタリングを一度挟みました。AIエージェントに実装を任せると、目先の要件は満たしつつも設計の一貫性は放っておくと崩れやすい、という実感を得た部分です。
一方で、何度指示しても直りきらなかったこともありました。
- チャットでやり取りした内容をそのままコード中のコメントに書き連ねてしまう癖。「実装の理由ではなく、会話のやり取りをコメントにしないで」と繰り返し伝えても、完全にはなくなりませんでした
- JSDocが冗長になり、同じくやり取りの経緯のような余計な記述が混ざる傾向。スキルとして規約を用意して指示しても、あまり改善が見られませんでした
コーディング規約をDocsやスキルとして整備しても、こうした細かい癖まで完全にコントロールしきれるわけではない、というのは正直な感想です。
コーディング規約をDocsとスキルで徹底する
この反省も踏まえて、コーディング規約をDocsとして文書化しました。ディレクトリ構成・コンポーネント設計・画像の命名規則などをDocs/配下にまとめ、Claude Codeにもこの規約に沿って実装させるようにしました。これにより実装がその場その場の思いつきにならず、後から読んでも一貫した構成になっています。ディレクトリ構成はおおよそこんな形です。
app/
posts/[slug]/page.tsx … 記事詳細
components/
Post/
PostCard/
PostCardContainer.tsx … データ取得・ロジック
PostCard.tsx … 表示専用
Game/
GameCard.tsx
Header.tsx
Footer.tsx
content/
posts/*.mdx
pages/*.mdx
lib/
posts.ts … コンテンツ取得
games.ts
imageUrl.ts
types/
content.tsデータ取得やロジックを持つコンポーネントは〇〇Container.tsx(コンテナ)と〇〇.tsx(表示専用)のペアに分け、記事・固定ページ・ゲームというコンテンツの種類ごとにドメインフォルダを分けています。
ディレクトリの切り方は基本的に、大元のPageコンポーネントを起点にして、そのページ専用のサブコンポーネントは親コンポーネントのフォルダの下にネストしていく「ドリルダウン構造」にしています。一方、複数のページ・機能から共通で使われるコンポーネントは、ページ配下に埋もれさせずコンテンツの種類(記事・ゲーム等)ごとのドメインフォルダに切り出す、という使い分けをしています。
Docsを書いただけでは「存在は知っているが読まれない」状態になりがちなので、Claude Codeの「スキル」機能(.claude/skills/)も使いました。「app/・components/配下を編集する前に必ずコーディング規約を読む」「images/配下や記事のfrontmatterを触る前に必ず画像命名規則を読む」というように、対象ファイルを編集しようとしたタイミングで該当するDocsへ自動的に誘導される仕組みにしてあります。規約を守るかどうかを都度指示し直さなくても、触っているファイルに応じて規約が勝手に参照される状態を作れたのは大きかったです。
移行で手放したもの
速度とコストを取った分、正直に手放したものもあります。
- コメント機能(スパム対策プラグインごと廃止)
- Google AdSense・Amazonアソシエイトによる広告収益
- 内容が薄かった一部の記事・タグ(機械学習系、イベントレポート系など)
もともと収益目的のサイトではなく、更新も止まっていたので、この部分は割り切って削ることにしました。
まとめ
塩漬けになった個人ブログでも、記事とゲームという資産さえ活かせれば、土台をまるごと静的サイトに作り直す価値はあると感じました。ポイントをまとめると
- レンタルサーバーの中身をまるごとローカルに落とし、Claude Codeに解析させてサイトマップを作らせてから設計に入ると、移行の手戻りが少ない
- 動的レンダリングを静的HTML配信に変えるだけで、パフォーマンススコア・サーバー代の両方が改善する。ただし画像などの重い要素をちゃんと最適化しないと、構成を変えただけでは逆に遅くなる部分も出てくる
- AIエージェントに実装を任せきりにすると責務や命名が崩れやすいので、途中でのリファクタリングは見込んでおく
- コーディング規約をDocs化し、運用スクリプトを汎用的な言語で書いておけば、特定のAIツールへの依存を切り離せる
同じように塩漬けになったWordPressサイトを持っている人の参考になれば幸いです。


