itch.io HTML5 CDN Still Serves WASM as application/octet-stream After Upload — How to Fix
Problem: itch.io shows upload successful, your zip layout looks correct, but the live demo still white-screens or hangs at the loader. curl -I on the deployed .wasm URL returns Content-Type: application/octet-stream (or an old ETag) minutes after you replaced the build.
Who is affected now: July 2026 teams running GX.games + itch + Steam parallel HTML5 lanes. You fixed MIME once using the Godot 4.5 boot-hang guide—that article covers first-upload streaming failures. This article covers CDN edge cache and “success” uploads that never update the bytes browsers fetch.
Fastest safe fix: Copy the live .wasm URL from DevTools → run curl -I (and one full GET hash if needed) → confirm application/wasm → if fail, re-upload with versioned filename or cache-bust query on index.html → file wasm_mime_receipt_v1.json with post_upload_mime_ok: true → align build_label with GX multi-channel receipts before sharing the itch link.
Direct answer
itch’s upload UI validates your package; the CDN edge may still serve stale objects or default MIME tables until the new object propagates. Browsers use WebAssembly.instantiateStreaming, which rejects application/octet-stream. Local python -m http.server tests use fresh files on disk— they do not prove the edge URL your players hit.
Why this issue spikes in July 2026
- Triple HTML5 storefronts — Marketing publishes itch before MIME receipt passes.
- Rapid re-upload loops — Fest week replaces
game.wasmwithout bumping cache keys. - GX + itch receipt stacks — 14 Free GX multi-channel tools added channel rows; itch MIME became the weak lane.
- Boot-hang help traffic — Teams apply Godot export fixes but skip post-upload
curl. - Forward resource brief — July itch WASM cross-check bookmark list pairs this help when shipped.
Symptoms and search phrases
- itch dashboard: “Upload complete.”
- Live game: loader hang or blank canvas.
curl -Ion CDN URL:application/octet-stream.ETag/Last-Modifiedunchanged after re-upload.- Incognito still broken; hard refresh sometimes works once.
- Works when downloading zip locally; fails on hosted URL only.
- Console:
instantiateStreamingMIME type error (same symptom, different root: edge vs first publish).
Root causes (check in order)
- CDN edge cache serving previous
game.wasmbytes or headers. - Browser cache on your test machine masking partial fixes.
- Wrong asset URL tested (old embed path, not latest upload slot).
- Zip root layout —
index.htmlnot beside.wasmon CDN path (upload “succeeded” but paths 404). - Renamed wasm in zip without updating Godot export HTML references.
- Parallel channel drift — itch build from yesterday; GX/Steam already promoted today (
build_labelmismatch in support tickets, not MIME). - Threaded export without itch SharedArrayBuffer — looks like hang after MIME fix; see COOP/COEP help.
Beginner path (first 30 minutes)
Prerequisites: Published itch HTML5 build, browser with DevTools, curl installed.
- Open itch Play in browser → F12 → Network → reload.
- Click
game.wasm(or your engine’s wasm name) → copy full URL. - Run
curl -I "<url>"— readContent-TypeandETag. - If
octet-stream→ follow Fastest safe fix (not another Godot re-export yet). - If
application/wasmbut still hangs → boot-hang MIME help + layout/404 checks.
Common mistake: Testing MIME on localhost or the zip on disk, not the itch CDN URL.
Fastest safe fix path
Step 1 — Post-upload proof table (CDN URL only)
curl -sI "https://your-name.itch.io/your-game/html5/game.wasm"
| Header | Pass | Fail signal |
|---|---|---|
HTTP/2 200 or HTTP/1.1 200 |
Yes | 404 → zip layout |
Content-Type: application/wasm |
Yes | application/octet-stream |
Content-Length > 0 |
Yes | 0 or tiny → wrong file |
ETag changes after re-upload |
Yes | Same ETag → stale edge |
Save output to release-evidence/02-html5/itch/curl-mime-<build_label>.txt.
PowerShell:
curl.exe -sI "https://your-name.itch.io/your-game/html5/game.wasm" | Select-String -Pattern "content-type|etag|last-modified" -CaseSensitive:$false
Step 2 — Cache-bust without guessing MIME
Option A — Versioned wasm filename (preferred for fest locks)
- Export to
game_v2026-07-rc2.wasm(engine-specific rename in export hook or post-step script). - Update
index.html/ Godot loader reference to match. - Re-upload zip; re-run Step 1 on new URL.
Option B — Query cache-bust on loader (quick jam fix)
In index.html (or engine config), append build label to wasm fetch:
// Example pattern — adapt to your export loader
const wasmUrl = "game.wasm?v=fest-demo-2026-07-rc2";
Re-upload; verify Network tab requests include ?v=.
Option C — New itch project page
When edge cache is stubborn, publish a new HTML5 target or draft page, smoke MIME, then swap the public link.
Step 3 — Zip layout verification
CDN path must expose:
index.html
game.wasm
game.pck (Godot) / data files per engine
*.js
Fail patterns: html5/html5/index.html double nesting; wasm only inside bin/ without loader path.
Step 4 — wasm_mime_receipt_v1.json
{
"schema": "wasm_mime_receipt_v1",
"checked_at_utc": "2026-05-24T23:30:00Z",
"build_label": "fest-html5-2026-07-rc2",
"host": "itch.io",
"wasm_url_tested": "https://your-name.itch.io/your-game/html5/game_v2026-07-rc2.wasm",
"curl_content_type": "application/wasm",
"post_upload_mime_ok": true,
"cdn_cache_bust": "versioned_filename",
"etag_changed_after_upload": true,
"incognito_smoke_ok": true,
"instantiate_streaming_ok": true,
"pass": true
}
Fail closed: post_upload_mime_ok: false blocks BUILD_RECEIPT HTML5 row promotion.
Step 5 — Multi-channel build_label alignment
When itch ships beside GX + Steam:
| Channel | Receipt field |
|---|---|
| itch | itch_upload_ok |
| GX | gx_upload_ok |
| Steam | steam_depot_upload_ok |
| All | channel_label_match: true |
Use one VERSION file; see GX receipt vs Steam build_label and triple-channel channel_label_match when itch is the third surface.
Step 6 — BUILD_RECEIPT + Wednesday smoke
Add columns to BUILD_RECEIPT:
| Column | Example |
|---|---|
wasm_mime_receipt |
pass |
post_upload_mime_ok |
true |
html5_surface |
itch_public |
Run Wednesday smoke with curl screenshot attached—not only in-browser play on your dev machine.
Working dev path — CI gate (engine-agnostic)
# Fail CI if live itch wasm URL wrong (store URL in secret/variable)
CTYPE=$(curl -sI "$ITCH_WASM_URL" | awk -F': ' '/^[Cc]ontent-[Tt]ype:/ {print $2}' | tr -d '\r')
test "$CTYPE" = "application/wasm" || exit 1
| Signal | Action |
|---|---|
| MIME OK, still hang | Thread/SAB, 404 on .pck, or tab-refocus OOM |
| MIME fail, new upload | Cache-bust Step 2 before re-export |
| Only embed broken | Parent iframe COOP; test full-page itch tab |
| GX works, itch fail | This article; do not merge channel receipts |
Verification checklist
- [ ]
curl -Ion CDN wasm URL →application/wasm - [ ]
ETagor content hash changes after re-upload - [ ] Incognito window boots to main scene
- [ ]
wasm_mime_receipt_v1.jsonpass: true - [ ]
channel_label_matchwhen multi-channel - [ ] BUILD_RECEIPT HTML5 row populated
- [ ] Marketing link updated only after receipt pass
Prevention
- Never announce itch “live” on upload success alone—require curl receipt.
- Version wasm filenames per fest RC (
game_vRC2.wasm). - Pair with July itch WASM cross-check resource when published.
- Separate boot-hang playbook (export preset) from CDN cache playbook (this page).
- Log itch wasm URL in
playtest_scope_map_v1.jsonnotes for facilitator repro.
Troubleshooting
| Symptom | Fix |
|---|---|
| Upload OK; curl still octet-stream | Cache-bust Step 2; new itch page |
| curl wasm OK; hang remains | Boot-hang + COOP + 404 checks |
| Hard refresh fixes once | CDN cache—version filename |
| MIME OK on GX, fail itch | Per-channel receipt; do not assume one fix covers all |
| Blank screen, no wasm request | index.html path wrong—zip layout |
FAQ
How is this different from the Godot 4.5 boot-hang MIME help?
That guide covers export preset, zip layout, and first-publish streaming failures. This guide covers post-upload CDN headers still wrong after itch reports success.
Will itch fix MIME on their side?
Treat MIME as your gate: prove with curl, cache-bust, and receipts. Self-host mirror if policy blocks your schedule.
Does application/wasm guarantee boot?
No—threads, missing .pck, and OOM are separate helps. MIME is necessary, not sufficient.
Same receipt as GX HTML5?
Use wasm_mime_receipt_v1.json for itch MIME; gx_html5_upload_receipt_v1.json for GX upload metadata—link both under one build_label.
Related links
- 12 Free itch.io HTML5 Upload Receipt and WASM MIME Cross-Check Tools (2026)
- itch.io Godot 4.5 WASM MIME Boot Hang (first-upload fix)
- itch.io HTML5 Build Blank Screen MIME WASM Entry
- GX.games Receipt build_id vs Steam build_label
- 14 Free GX.games Multi-Channel HTML5 build_label Receipt Tools
- Godot 4.5 Web Export WASM Memory OOM After Tab Refocus
- Godot Web Export SharedArrayBuffer COOP COEP Fix
- 2026 H2 HTML5-First Studios Second Storefront Analysis
- Wednesday Demo Build Smoke Ritual
- Your First BUILD_RECEIPT JSON and Upload Log
- Official: itch.io — Uploading HTML5 games, MDN instantiateStreaming
Prove curl -I on the CDN wasm URL after every itch upload—not the green checkmark in the uploader alone.