Host a Godot or Unity web build
Your web export online with the headers it needs, on the free plan.
GitHub Pages cannot set the response headers a game
engine asks for. A threaded Godot 4 export refuses to start there, and a
Unity Brotli or Gzip build downloads as unreadable bytes. The
game-web-starter template fixes that with two small
files in your export folder, a Dockerfile and a
Caddyfile. This template is not affiliated with Godot or
Unity.
Why the headers matter
-
Godot threads: a Godot 4 export with Thread Support
only runs when the page is cross-origin isolated. The server sends
Cross-Origin-Opener-Policy: same-originandCross-Origin-Embedder-Policy: require-corpon every path. -
Unity Brotli and Gzip: the browser can only unpack
.brand.gzfiles when the server sendsContent-Encodingand the type of the file inside. The server does that for the names Unity writes. -
File types:
.wasmis sent asapplication/wasmand.pckasapplication/octet-stream. Every file is sent withCache-Control: no-cache, so a new build shows up on the next load and unchanged files cost one quick check.
The build compresses your game once, when the app is built, so every player gets the small download and the running app does no compression work. Each player gets the Brotli or Gzip copy their browser accepts, and the original if it accepts neither. A file that is already Brotli or Gzip is left alone.
Journey 1: deploy the button, then use your own build
- Open the deploy link and sign in if asked. Under App size, keep 256 MB. The free plan is enough.
-
Click Deploy and wait until the app shows as running.
Open its address and play Meteor Run, a small Godot 4 arcade game:
steer with the arrow keys or by dragging, shoot the meteors. The corner
says
Cross-origin isolated: yesandThreads: on, which is what the headers make possible. - Click Use this template on GitHub to make your own copy, connect it in Dockhold, and deploy it.
-
In your copy, replace the demo files at the root with your export. Keep
Dockerfile,Caddyfile,.dockerignoreand theGODOT-LICENSE.txtandGODOT-COPYRIGHT.txtnotices. You can deletedemo-src/. - Commit and push. Every push to your main branch redeploys the app.
Journey 2: no git
- Export your game into a folder. The entry page must be named
index.html. -
Copy the template's
DockerfileandCaddyfileinto that folder, next toindex.html. They are the only two files you add. -
From that folder, run:
npx dockhold login npx dockhold deployloginopens a browser once.deployuploads the folder, compresses it while building, and prints the address of your app. Runnpx dockhold deployagain to ship a new build.
The entry page must be index.html
The server serves your export from its root, and the page people open is index.html.
-
Godot: in Project > Export, choose
the Web preset and, when asked for the file name, use
index.html. Godot writesindex.js,index.wasm,index.pckand the other files next to it. -
Unity: a WebGL build writes
index.htmland aBuild/folder. Use the build output folder as it is.
Unity notes
Build the WebGL player with Compression Format set to
Brotli or Gzip, and leave Decompression Fallback off. The
.br and .gz files, including
.symbols.json.br, work as built. Unity needs no change to the
build.
Turn cross-origin isolation off
Isolation is what lets a threaded Godot build run. It also stops the page
from embedding third-party content that does not opt in, such as some ad
or video frames. If your page embeds such content and your game does not
use threads, open the app's Variables tab, add
CROSS_ORIGIN_ISOLATION with the value off
(exactly that, lowercase), and
restart the app. The two headers are gone from every response, and the
file type and encoding rules stay. Remove the variable to turn isolation
back on.
Options
The free app is the full template. With a paid plan you can also add:
- A private playtest link. Make the app private on its Access tab. Visitors see a browser login prompt before the game loads, and you give out a token as the password.
- Your own domain. Add a custom domain on the app's Domains tab, so the game lives on your own site's address.
Troubleshooting
-
A threaded Godot build does not start: it needs the
page to be cross-origin isolated. Check that
CROSS_ORIGIN_ISOLATIONis not set tooffon the Variables tab. - A page embeds an ad or video frame that stops loading: isolation blocks third-party content that does not opt in. See the section above.