Construct 3 HTML5 WASM Fetch Blocked by CORS on Custom itch.io Domain - How to Fix
Problem: Your Construct 3 HTML5 export loads on yourgame.itch.io but shows a blank canvas on your custom itch domain (play.yourstudio.com). Browser DevTools reports CORS failures fetching data.wasm, worker scripts, or other runtime assets. Local preview and the itch subdomain URL work.
Who is affected now: July–October 2026 HTML5-first teams enable itch custom domains for fest press kits while running GX.games + Steam parallel lanes (triple-channel BUILD_RECEIPT help). This is origin / hosting topology, not post-upload WASM MIME (application/octet-stream on CDN) and not Construct NW.js Steam save paths (desktop export).
Fastest safe fix: Prove green on *`.itch.iosubdomain** first → file **cors_smoke_receipt_v1.json** withsubdomain_smoke_ok: true` → either keep gameplay on subdomain (marketing custom domain links there) or advance to custom domain CORP/COEP preflight only with a documented header plan.
Direct answer
itch custom domains change the page origin players see. Construct’s HTML5 runtime fetches wasm and workers from URLs that may be treated as cross-origin unless assets and headers align. yourgame.itch.io uses itch’s game hosting model; arbitrary CNAME branded domains often break same-origin assumptions. Fix the hosting decision, not another Construct re-export loop.
Why this issue spikes in July–October 2026
- Custom domain press kits — Marketing shares
play.brand.combefore engineering runs subdomain smoke. - Construct HTML5 fest velocity — Teams ship browser demos before NW.js Steam builds.
- MIME vs CORS confusion — Teams pass
curlWASM MIME on subdomain then fail on custom domain with CORS only. - Multi-channel receipts — Wrong itch URL invalidates
channel_label_matchsupport tickets. - October async playtest volume — Facilitators distribute mixed subdomain + custom links in one Discord thread.
Symptoms and search phrases
- Console:
Access to fetch at '.../data.wasm' from origin 'https://play.brand.com' has been blocked by CORS policy. - Network tab: wasm or worker requests red with CORS failure (not 404).
- Same zip: works on
studio.itch.io/demo, fails on custom domain. - Local preview works (different origin rules).
- After enabling custom domain in itch settings only—no Construct project change.
- COEP / CORP errors appear when experimenting with cross-origin isolation on custom host.
Root causes (check in order)
- Custom domain origin ≠ wasm asset origin (split hosting).
- Marketing CNAME live before subdomain proof completed.
- Worker script loaded from a host without
Access-Control-Allow-Originfor page origin. - Wrong itch upload slot tested (old embed path).
- MIME fixed but origin not —
application/wasmon CDN does not fix cross-origin fetch. - Third-party iframe wrapping itch runner with extra origin layer.
- Construct export pointed at absolute URLs hard-coded to subdomain while page served on custom domain.
Beginner path (first 30 minutes)
Prerequisites: Published Construct HTML5 build on itch, browser DevTools, optional curl.
- Open
https://yourgame.itch.io/your-demo→ confirm game runs. - Open custom domain URL → F12 → Console → note CORS error URL.
- Compare origins in error (page origin vs blocked asset URL).
- If subdomain fails too → fix itch CDN MIME or export layout first.
- If subdomain passes → run subdomain smoke preflight (S1–S6).
- Decide: defer custom domain for fest week or plan CORP/COEP chapter.
Common mistake: Re-uploading the Construct zip five times without changing which URL you share.
Fastest safe fix path
Step 1 — Subdomain proof (required gate)
Follow Construct 3 itch subdomain WASM and CORS smoke (guide):
| Gate | Pass |
|---|---|
S1 — Game loads on *.itch.io |
Canvas interactive |
| S2 — DevTools no CORS on wasm/worker | Console clean |
S3 — curl -I wasm MIME |
application/wasm where applicable |
S4 — cors_smoke_receipt_v1.json |
subdomain_smoke_ok: true |
Policy: Do not debug custom domain until S4 is green.
Evening tutorial: Your First Construct 3 itch Subdomain WASM Smoke.
Step 2 — Origin map (write before changing DNS)
Create cors_origin_map_v1.json in release-evidence/html5/:
{
"schema": "cors_origin_map_v1",
"page_origin_subdomain": "https://yourgame.itch.io",
"page_origin_custom": "https://play.yourstudio.com",
"wasm_url_observed": "https://...",
"worker_url_observed": "https://...",
"same_origin_on_subdomain": true,
"same_origin_on_custom": false,
"hosting_decision": "defer_custom_use_subdomain_for_fest"
}
Strategy playbook: Construct custom domain CORS vs subdomain hosting.
Step 3 — Fest-week defer (recommended for most indies)
| Action | Why |
|---|---|
Press kit links to yourgame.itch.io/demo |
Proven origin |
| Custom domain → marketing landing only | No wasm on custom origin |
File hosting_decision: defer_custom on receipt |
Diligence traceability |
Course milestone: Lesson 208 — CORS hosting decision receipt.
Step 4 — Custom domain header path (advanced only)
If you must serve gameplay on custom domain:
- Complete custom domain worker CORP/COEP preflight.
- Document three-origin map (page, wasm, worker).
- Verify COOP/COEP requirements for Construct features you use.
- Pair with fest CORP/COEP worker smoke.
Honest limit: itch-hosted custom domains may not expose header knobs you need. If CORP/COEP cannot pass, defer custom—do not ship a broken branded URL.
Step 5 — Receipt cors_smoke_receipt_v1.json
{
"schema": "cors_smoke_receipt_v1",
"checked_at": "2026-05-27T18:00:00Z",
"engine": "construct-3-html5",
"subdomain_smoke_ok": true,
"custom_domain_smoke_ok": false,
"hosting_decision": "defer_custom_use_subdomain_for_fest",
"build_label": "2026-05-27-fest-html5-03",
"facilitator": "initials"
}
Wire optional BUILD_RECEIPT column html5_cors_smoke_ok when HTML5 is in scope for Wednesday smoke.
Working dev path — DevTools proof table
| Check | Where | Pass |
|---|---|---|
| Page URL origin | Address bar | Note scheme + host |
| Failed request URL | Network → wasm/worker | Compare host to page |
Access-Control-Allow-Origin |
Response headers (if visible) | Matches page or * |
| Subdomain run | *.itch.io |
No CORS errors |
| Custom run | branded domain | Document fail reason |
| Receipt | cors_smoke_receipt_v1.json |
subdomain_smoke_ok: true |
Godot comparison: Godot WASM memory ceiling — OOM vs CORS are different triage paths.
Verification checklist
- [ ] Game runs on itch subdomain with clean console
- [ ] CORS error URLs captured for custom domain attempt
- [ ]
cors_smoke_receipt_v1.jsonfiled with explicithosting_decision - [ ] Press / Discord links use proven subdomain URL for fest week
- [ ] MIME proof on live wasm URL if loader hangs without CORS (itch MIME help)
- [ ]
build_labelmatches GX multi-channel receipts when dual-publishing - [ ] Facilitator README states approved hosting pattern
Prevention
- Run subdomain smoke preflight before enabling custom domain in itch.
- Add hosting decision row to 12-free itch WASM cross-check resource facilitator checklist.
- Never share custom domain in fest kit until
custom_domain_smoke_ok: trueor decision is defer_custom. - Pair marketing with CORS vs subdomain playbook decision tree.
- Separate HTML5 itch from NW.js Steam save-path testing (Construct NW.js save help).
Troubleshooting
| Symptom | Fix |
|---|---|
| CORS on custom only | Defer custom; use subdomain gameplay URL |
| CORS on subdomain too | MIME / layout / 404 — not this article |
| 404 on wasm, no CORS | Zip root layout; re-upload |
| Works in editor, fails hosted | Test hosted URL only |
| COEP/CORP errors on custom | CORP/COEP chapter or defer custom |
| GX works, itch custom fails | Independent channels—prove itch separately |
FAQ
Can I fix CORS by re-exporting Construct?
Usually no if subdomain already works. Fix which URL you publish.
Does itch support custom domains for HTML5 wasm?
Custom domains are a marketing/hosting feature with origin constraints. Prove with receipts; defer if headers cannot pass.
Is this the same as Godot wasm MIME errors?
No—Godot MIME is Content-Type on fetch. Construct custom domain is cross-origin blocking.
Should playtests use custom domain links?
Use subdomain until custom_domain_smoke_ok: true is filed.
Related links
- Construct subdomain WASM CORS smoke (guide)
- Construct custom domain CORP/COEP headers (guide)
- Construct CORS vs subdomain playbook (blog)
- Your First Construct itch Subdomain WASM Smoke (blog)
- itch.io CDN WASM MIME after upload (help)
- Lesson 208 — CORS hosting decision (course)
- Lesson 222 — CORP/COEP fest receipt (course)
- 12 Free itch HTML5 WASM MIME cross-check tools
- 14 Free GX.games multi-channel HTML5 tools
- Official: MDN CORS, Construct 3 manual — Publishing
Prove subdomain first, document hosting_decision, then touch custom domains—CORS on branded itch URLs is a hosting choice, not a broken export.