UnityのWebゲームを自前サーバーで公開する(Caddyの設定と実測)
Unity の WebGL ビルドを自分のサーバーで配信するときに必要な設定を実測で残します。.br に Content-Encoding と application/wasm を付ける、末尾スラッシュの 308、前段の圧縮との二重がけ、アップロードと反映にかかった時間。
公開 2026年9月16日
この記事の要点4 points
- Unity の WebGL ビルドを自前で配るときに必要なのは、圧縮済みファイルに Content-Encoding と正しい Content-Type を返すことだけ。
- Caddy なら10行ほど。 末尾スラッシュの 308 リダイレクトは handle_path の外に置く。
- 前段の Caddy に圧縮設定があっても、すでに Content-Encoding が付いた応答は二重に圧縮されなかった。
- 約10MB のアップロードは1秒、push から外形確認まで8秒。 配信コンテナのメモリは 11.6MiB。
目次7章
検証した環境最終確認 2026年9月16日
- Unity 6.6(6000.6.0f1)の WebGL ビルド(Brotli・Decompression Fallback なし)
- Caddy 2(Docker)
- 2段構成(前段で TLS 終端)
書いてあるのは、この環境で実際に試した結果です。ツールの仕様や料金は変わるので、重要な判断の前には公式の情報も確認してください。
Unity の Web ビルドを、自分のサーバーに置いて公開しました。unityroom や itch.io に上げるのではなく、自分のドメインで配る形です。
やってみて分かったのは、これでした。
必要な設定は、ほぼ「圧縮済みファイルに正しいヘッダーを返すこと」だけ。
Caddy なら10行ほどです。ただし、そこを外すとブラウザは無言で読み込みに失敗します。
構成
STEP 01
ブラウザ
STEP 02
前段の Caddy
TLS・HSTS・gzip/zstd
STEP 03
サービスの Caddy
静的配信・ヘッダー付与
STEP 04
ビルドのファイル
アップロードで置くだけ
ゲームのビルドは git に入れていません。約10MB あり、ビルドのたびに中身が変わるからです。サーバーの公開用フォルダに置いて、Caddy から静的に配るだけにしています。
肝心の設定(これだけ)
# /play/<ゲーム> → /play/<ゲーム>/(handle_path の前に置く)
@gameNoSlash path_regexp gameNoSlash ^/play/([a-z0-9-]+)$
redir @gameNoSlash /play/{re.gameNoSlash.1}/ 308
handle_path /play/* {
root * /srv/games
@br path *.br
header @br Content-Encoding br
@wasm path *.wasm *.wasm.br
header @wasm Content-Type application/wasm
@js path *.js.br
header @js Content-Type application/javascript
@data path *.data *.data.br
header @data Content-Type application/octet-stream
header Cache-Control "no-cache"
file_server
}
ポイントは3つです。
- 1
.br には Content-Encoding: br を付ける
Unity の Brotli ビルドはWebGL.wasm.brのような圧縮済みファイルを出す。サーバーが「圧縮されている」と伝えないと、ブラウザはただのバイナリとして受け取って失敗する - 2
中身に合った Content-Type も付ける
.wasm.brは放っておくとapplication/octet-streamになる。application/wasmを返さないと、高速な読み込み経路(ストリーミングコンパイル)が使えない - 3
末尾スラッシュのリダイレクトは handle_path の外に置く
handle_pathの中では接頭辞/playが外れているので、中に書くと接頭辞の落ちた URL に飛ぶ
実測したヘッダー
| パス | 結果 |
|---|---|
/play/breakout | 308 → /play/breakout/ |
…/Build/WebGL.wasm.br | 200・Content-Encoding: br・Content-Type: application/wasm |
…/Build/WebGL.data.br | br・application/octet-stream |
…/Build/WebGL.framework.js.br | br・application/javascript |
…/Build/WebGL.loader.js | gzip(前段の Caddy が圧縮)・text/javascript |
分かったこと。
file_serverは、先にheaderで入れたContent-Typeを上書きしませんでした。 期待どおりの動きです- 前段の Caddy に
encode gzip zstdがあっても、すでにContent-Encoding: brが付いた応答は二重に圧縮されませんでした
設定ファイルはフォルダごとマウントする
Docker で Caddy を動かすとき、最初は Caddyfile を1ファイルだけマウントしていました。
ファイル1つをマウント
volumes: - ./Caddyfile:/etc/caddy/Caddyfileデプロイで
git checkout -f がファイルを作り直すと、コンテナは古い中身を見続ける(ファイルの実体が置き換わるため)フォルダごとマウント
volumes: - ./caddy:/etc/caddy作り直されても新しい中身が見える
反映にかかった時間
1秒
ビルドのアップロード
約10MB を tar でまるごと
8秒
デプロイ
設定の検証〜外形確認まで
11.6MiB
配信コンテナのメモリ
起動直後
64MiB
メモリの上限
静的配信ならこれで足りる
アップロードは tar で固めてそのまま展開する形にしています。差分同期ではなく毎回まるごと置き換えですが、10MB 程度ならこのほうが事故が少ないです。
サイズの話
公開した時点では、WebGL.wasm.br が 7.3MB(展開すると 38.3MB)、全体で約 10.1MB ありました。スマホの回線では重いので、このあと削っています。
検索に出す/出さないの整理
ゲーム本体のページ(/play/…)は X-Robots-Tag: noindex を付けて検索に出していません。中身は Unity が生成した HTML で、説明も見出しもないからです。検索に出すのは、制作の記録を書いた紹介ページのほうにしています。
代わりに、ゲームを直接開いた人のために、上部に戻る帯を差し込んでいます。ビルドのたびに index.html は作り直されるので、アップロードのスクリプトの中で毎回差し込む形にしました。
このゲームを作った過程はブロック崩しのページに、環境まわりで踏んだ問題はWindowsの落とし穴14個にあります。
FAQ
よくある質問
Unity の WebGL ビルドをそのまま置くだけでは動きませんか?
サーバーが .br のファイルに Content-Encoding: br を返さないと、ブラウザは圧縮されたデータをそのまま解釈しようとして失敗します。ヘッダーを設定できないサーバーでは、Unity 側で Decompression Fallback を有効にしてビルドしてください。
前段のサーバーで gzip 圧縮していると二重圧縮になりませんか?
実測では、すでに Content-Encoding: br が付いた応答は二重に圧縮されませんでした。心配な場合は curl でヘッダーを確認してください。
ゲームのページは検索に出したほうがいいですか?
ゲーム本体のページは Unity が生成した HTML で説明が無いため、noindex にしています。検索に出すのは、制作の記録を書いた紹介ページのほうです。
この記事は役に立ちましたか?
NEXT
あわせて読む
AIにUnityのテスト・動作確認・ビルドまで回させる手順
Unity CLI の run_tests / editor_play / capture_game_view / build を使って、人が画面を触らずにテストと動作確認とWebGLビルドを回した手順を、実際のコマンドと所要時間つきで残します。
Claude CodeとCodexに同じ仕様書でブロック崩しを作らせた
同じ仕様書1枚を Claude Code(Opus 5)と Codex(GPT-5.6 Sol)に渡し、Unity 6.6 でブロック崩しを作らせて比べました。かかった時間、テストの数、詰まった場所、消えた利用枠まで、実測値をそのまま載せています。
UnityのWebビルドを10.1MB→7.4MBにした(−27.3%・2段階の実測)
まず設定2つで 10,143,315 → 7,945,839 バイト。次に未使用パッケージ39件を外して 7,374,207 バイト。合計 −27.3% です。効かなかった施策(スプラッシュ停止)も数字ごと残します。