AIゲーム制作ラボ
公開・収益化6分で読めます

UnityのWebゲームを自前サーバーで公開する(Caddyの設定と実測)

Unity の WebGL ビルドを自分のサーバーで配信するときに必要な設定を実測で残します。.br に Content-Encoding と application/wasm を付ける、末尾スラッシュの 308、前段の圧縮との二重がけ、アップロードと反映にかかった時間。

Libra

公開 2026年9月16日

この記事の要点4 points

  1. Unity の WebGL ビルドを自前で配るときに必要なのは、圧縮済みファイルに Content-Encoding と正しい Content-Type を返すことだけ
  2. Caddy なら10行ほど。 末尾スラッシュの 308 リダイレクトは handle_path の外に置く。
  3. 前段の Caddy に圧縮設定があっても、すでに Content-Encoding が付いた応答は二重に圧縮されなかった
  4. 約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行ほどです。ただし、そこを外すとブラウザは無言で読み込みに失敗します。

構成

  1. STEP 01

    ブラウザ

  2. STEP 02

    前段の Caddy

    TLS・HSTS・gzip/zstd

  3. STEP 03

    サービスの Caddy

    静的配信・ヘッダー付与

  4. STEP 04

    ビルドのファイル

    アップロードで置くだけ

ゲームはアプリケーション(Next.js)を通さない。数十MBの配信を Node にさせない。

ゲームのビルドは 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. 1

    .br には Content-Encoding: br を付ける

    Unity の Brotli ビルドは WebGL.wasm.br のような圧縮済みファイルを出す。サーバーが「圧縮されている」と伝えないと、ブラウザはただのバイナリとして受け取って失敗する
  2. 2

    中身に合った Content-Type も付ける

    .wasm.br は放っておくと application/octet-stream になる。application/wasm を返さないと、高速な読み込み経路(ストリーミングコンパイル)が使えない
  3. 3

    末尾スラッシュのリダイレクトは handle_path の外に置く

    handle_path の中では接頭辞 /play が外れているので、中に書くと接頭辞の落ちた URL に飛ぶ

実測したヘッダー

パス結果
/play/breakout308/play/breakout/
…/Build/WebGL.wasm.br200・Content-Encoding: brContent-Type: application/wasm
…/Build/WebGL.data.brbrapplication/octet-stream
…/Build/WebGL.framework.js.brbrapplication/javascript
…/Build/WebGL.loader.jsgzip(前段の 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.br7.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