
本番だけ動かない…Cloudflare Pagesの初回デプロイでつまずいた話
こんにちは、よなです。
YONA GAMESという個人ゲームサイトを運営しています。
前回は、GoogleやGitHubの認証情報をそのままゲームのユーザー管理に使わず、Supabaseの内部ユーザーIDだけを保存することにした話を書きました。設計方針は固まった。CloudflareとSupabaseの役割分担も整理できた。あとは公開するだけ。
そう思っていたのですが、「公開する」という作業に、ボタンなんて一つも存在しませんでした。あるのは、次々と現れるエラー画面だけです。
今回つまずいたのは、Cloudflare Pagesへの初回デプロイです。ローカルでは動く。ビルドも通る。なのに、本番だけ動かない。「ローカルでは動くんですけどね」は、開発の現場で一番信用してはいけない台詞だとわかっていたはずなのに、まさか自分がこんなに早くその台詞を心の中でつぶやくことになるとは思っていませんでした。
まずはドメインを決める
サイトを公開するには、当然URLが必要です。最初に考えていたのは yona.games でした。YONA GAMESという名前に、一番自然な響きだと思っていました。
ところが、取得価格を見ると年間26ドル以上。個人運営で、できるだけ無料枠中心に構成したいと考えていたので、ドメインだけ贅沢するのも変な話です。

いくつか候補を比較した結果、yona-games.com が10ドル程度で取得できることがわかりました。名前としても意味が通るし、今後ゲームが増えても使い回せる。最終的にこちらを選びました。ゲーム一本の名前をドメインにするか、ゲームを置く場所そのものをブランドにするか。この選択で、後者を取った形です。

サイトとゲームは別領域
次に考えたのが、サイトとゲームの配置です。最終的な構成はこうなりました。
Cloudflare Pages → YONA GAMESのサイト
Cloudflare Worker → MINECOIN Miner本体
https://yona-games.com/minecoin-miner/サイトはCloudflare Pagesで公開し、ゲーム本体は別のCloudflare Workerとして配信する。見た目は同じドメイン配下ですが、中身は完全に別の配信単位です。
build:productionが存在しない
サイト側のリポジトリをCloudflare Pagesへ接続し、ビルド設定と環境変数を入力しました。手順だけ見ればそこまで難しくなさそうです。ローカルではすでにサイトが動いていて、本番用のビルドもローカルでは成功していたので、Cloudflare Pagesでも同じように通るはずだと思っていました。
初回デプロイを実行。しばらく待って出てきたのは、こんなエラーでした。
Missing script: "build:production"原因は、Cloudflare Pagesのビルド設定で指定していた build:production というスクリプトが、サイト側の package.json に存在していなかったことでした。つまり私がローカルで「ビルド成功」と思っていたのは、正確には自分が手元で実行していた別のコマンドが成功していただけだったんです。
次は環境変数で止まる
ビルドの問題を直し、再デプロイ。今度は本番環境変数のチェックで失敗しました。引っかかったのは主に次の項目です。
VITE_SUPABASE_URL
VITE_SUPABASE_PUBLISHABLE_KEY
VITE_APP_ENV
SupabaseのURLや公開キーが未設定、または形式が正しくない。VITE_APP_ENV が本番用の値になっていない。ローカルの .env ファイルにはちゃんと値が入っているのですが、そのファイルがそのままCloudflare Pagesに引っ越してくるわけではありません。ローカルの環境変数と本番の環境変数は別物です。
一つずつ値を確認し、Cloudflare Pages側へ正しく登録し直して、ようやくビルドが成功しました。
このエラー、公開前に見つかってむしろよかったと思っています。もし気づかないままビルドだけ通っていたら、公開後に「ログインできない」「セーブデータが保存されない」という形で発覚していたはずです。以後は、本番ビルド時に必要な環境変数を検証し、足りなければデプロイ前にエラーで止まる仕組みを入れました。「動かないものを公開してあとから気づく」より「公開前に止まる」ほうが、圧倒的に安全です。
サイトを更新してもゲームは更新されない
サイト側が無事動き始めたあと、もう一つ重要な事実に気づきました。サイトのリポジトリを更新しても、ゲーム側のCloudflare Workerは自動では更新されません。サイトとゲームを分離した以上、それぞれに個別のデプロイが必要でした。
同じ yona-games.com の中にあるからといって、同じデプロイ単位ではない。サイト側だけ更新すると、ページ上の説明は新しくなっているのに、ゲーム本体は古いまま、という状態が起こり得ます。看板だけ新装開店で、店の中の商品棚は先月のままみたいな状況。利用者からすると、かなり不安な光景です。
サイトとゲームを分けたのは、将来ゲームが増えたときの管理を見越した判断でした。ただ、分けたら分けたで、更新手順も別々に管理する責任がついてきます。構成を分けるメリットと、把握すべきことが倍になるデメリットはトレードオフでした。
本番でだけ壊れる画像とOGP
ビルドとデプロイが通ったあとも、確認事項は残っていました。ローカルでは問題なかったサイトトップのYONA GAMESロゴが、本番のシークレットウィンドウでだけ表示されない不具合が発生。原因は画像の参照先がローカル環境を前提にしたパスになっていたことでした。ゲームは https://yona-games.com/minecoin-miner/ 配下で配信されるため、サイトのルート基準の参照と、ゲームの配信パス基準の参照とでは結果が変わってきます。import.meta.env.BASE_URL を使い、配信されているパスを基準に参照するよう修正しました。ローカルの画面をいくら見つめても、絶対に見つからない種類の不具合でした。
SNSやnoteに貼ったときのOGP画像でも似た問題が起きました。ゲーム側の画像とサイト側の画像が混同して表示されることがあり、原因が画像自体なのか参照先なのかキャッシュなのか、一つずつ切り分ける必要がありました。最終的にはサイト用の画像を専用に用意して解決しています。本番環境の厄介なところは、コードを直しても画面がすぐには変わらないことです。キャッシュという名の残り香が、しばらく居座り続けます。
一番の教訓
今回わかったのは、「ローカルでは動いた」という報告だけでは、何の証明にもならないということです。実行するビルドコマンド、環境変数の登録場所、配信されるURLの階層、静的ファイルの参照パス、キャッシュ、サイトとゲームのデプロイ単位。どれか一つでもズレれば、結果は変わります。ローカルで動くことは必要条件であっても、十分条件ではありません。
逆に言えば、配信単位さえ理解してしまえば、混乱の大半は整理できます。どのリポジトリを更新するのか、どのコマンドでビルドするのか、どのサービスへデプロイするのか、どのURLで確認するのか。これを明確にするだけで、「どこを直せばいいのかわからない」という迷子状態からは抜け出せます。
ゲームを作ることと、ゲームを公開することは別の競技です。そして公開という競技では、最後まで「自分の環境では動いた」を疑ってかかる必要がある。それが、初回デプロイで身をもって学んだことでした。
次回は、Googleログインやゲストプレイ、Turnstileの設定で起きた問題について書く予定です。ゲストで始められるようにしたはずなのに、CAPTCHAエラーで開始できない。ゲーム本体の実装とは別の場所で、また別の地雷を踏むことになります。
ではまた!
PC専用ブラウザゲーム『MINECOIN Miner』絶賛公開中!
ソシャゲ風マインスイーパーで鉱石の収集・スキルで素早く盤面攻略・深層探査などのやりこみ要素もあります!ぜひプレイしてみてください。
