Godot Web Export Fails with SharedArrayBuffer Error - COOP COEP Header and Hosting Config Fix - How to Fix

Problem: Your Godot project exports to HTML5, you upload it, and the browser console shows errors about SharedArrayBuffer, cross-origin isolation, Cross-Origin-Opener-Policy, or Cross-Origin-Embedder-Policy. The page may stay on a loader, show a blank canvas, or fail before gameplay starts.

This almost always means the browser is blocking features that require a cross-origin isolated document, while your host is not sending the right response headers (or you enabled threads in the export without a host that can satisfy isolation).

Root Cause

Modern Chromium-based browsers only expose SharedArrayBuffer (and related threading primitives) when the top-level page is cross-origin isolated. That requires compatible headers, typically:

  • Cross-Origin-Opener-Policy: same-origin (COOP)
  • Cross-Origin-Embedder-Policy: require-corp (COEP) or a carefully chosen credentialless setup that still achieves isolation

Godot’s threaded HTML5 export path relies on these capabilities. If threads are enabled in the export preset but your static host serves plain files with no isolation headers, the engine cannot initialize the threaded runtime and you see SharedArrayBuffer is not defined or similar messages.

Less commonly, COEP breaks subresources (iframes, third-party scripts, fonts, or assets) that are not marked crossorigin or served with Cross-Origin-Resource-Policy where required, which can look like a hang or silent failure after headers are added.

Quick Fix Checklist

  1. Open DevTools → Console and capture the exact SharedArrayBuffer or COOP/COEP wording.
  2. In Project → Export → Web, confirm whether threads (or equivalent threaded export option for your Godot minor version) are on or off.
  3. If threads are on, add COOP + COEP at the same origin that serves index.html (not only on a CDN subdomain).
  4. Reload with cache disabled and verify crossOriginIsolated is true in the console.
  5. If you cannot control headers (for example some embedded hosts), disable threads and rebuild.

Step 1 - Confirm whether you need cross-origin isolation

  1. Export a Web build with your current settings.
  2. Open the hosted game (HTTPS strongly recommended).
  3. In the console, run:
window.crossOriginIsolated
  • If this prints false but your preset uses threads, expect SharedArrayBuffer failures until headers are fixed.
  • If you do not use threads and still see isolation errors, capture the stack trace; you may be on an older template path or a host injecting incompatible iframes.

Step 2 - Set COOP and COEP on the document that loads Godot

Apply headers on responses for index.html (and ideally the whole static app prefix) from the same site players use in the address bar.

nginx

Inside the server or location that serves your game:

add_header Cross-Origin-Opener-Policy "same-origin" always;
add_header Cross-Origin-Embedder-Policy "require-corp" always;

Reload nginx and verify with curl:

curl -I https://your-domain.example/path/index.html

You should see both headers on the HTML response.

Apache (.htaccess or VirtualHost)

Header set Cross-Origin-Opener-Policy "same-origin"
Header set Cross-Origin-Embedder-Policy "require-corp"

Ensure mod_headers is enabled.

Netlify (netlify.toml)

[[headers]]
  for = "/*"
  [headers.values]
    Cross-Origin-Opener-Policy = "same-origin"
    Cross-Origin-Embedder-Policy = "require-corp"

Deploy, then confirm headers on the exact URL players open.

Vercel (vercel.json)

{
  "headers": [
    {
      "source": "/(.*)",
      "headers": [
        { "key": "Cross-Origin-Opener-Policy", "value": "same-origin" },
        { "key": "Cross-Origin-Embedder-Policy", "value": "require-corp" }
      ]
    }
  ]
}

Cloudflare

Use Transform Rules → Modify Response Header (or Workers) to append the same two headers for your game’s route. Confirm they appear on the HTML response, not only on static assets.

Important: Headers must be present on the top-level navigation response. A CDN that only adds headers to .wasm but not to index.html will still fail isolation checks.

Step 3 - Align Godot export options with your hosting reality

  1. Open Project → Export → Web (HTML5).
  2. Locate the threading / threads option for your Godot version.
  3. Choose one path:

Path A - You control headers (recommended for performance)
Keep threads enabled and keep Step 2 headers in place across your deploy.

Path B - You cannot set headers
Turn threads off, re-export, and upload again. This removes the hard dependency on SharedArrayBuffer for typical Godot web builds.

Step 4 - Fix COEP breakages from cross-origin subresources

After enabling COEP, some embeds or assets fail until they are compatible:

  • Serve game binaries (.wasm, .pck, .js, workers) from the same origin as index.html when possible.
  • Any cross-origin image, audio, or script may need correct crossorigin attributes and CORP/CORS behavior from the third party.
  • Remove stray analytics snippets or chat widgets from the export wrapper while debugging; reintroduce them only if they tolerate require-corp.

If you must load cross-origin media that cannot be marked CORP-safe, Cross-Origin-Embedder-Policy: credentialless is sometimes used as an alternative isolation strategy, but it has different compatibility trade-offs—validate in Chrome and Firefox against your exact asset mix.

Verification

  1. Hard refresh the game (Ctrl+Shift+R / Cmd+Shift+R).
  2. Confirm window.crossOriginIsolated === true in the console.
  3. Confirm the game reaches main menu or first scene without SharedArrayBuffer errors.
  4. Run a mobile smoke test; Safari’s isolation rules differ over time—treat mobile as a separate verification pass.

Alternative Fixes

  • Disable threads and ship a single-threaded web build when headers are impossible on your current host.
  • Move hosting to a provider that supports custom response headers on static sites (see examples above).
  • Avoid nested iframes that strip isolation (some embed players wrap your page in a non-isolated parent).

Prevention

  • Decide threads vs no-threads before you lock art and performance budgets; threading changes can alter stutter profiles on web.
  • Add a release checklist item: “Headers verified on production URL,” not only on staging paths.
  • Keep HTML, WASM, PCK, and workers path-stable across deploys to reduce cache confusion after header updates.

Related Reading

Bookmark this page if you ship Godot HTML5 builds regularly. Share it with teammates who manage hosting so engine and infra fixes stay aligned.

FAQ

Do I need COOP and COEP if threads are disabled?

Usually no. Isolation errors tied to SharedArrayBuffer should disappear when threading is off, unless another part of your page still requires isolation.

Why does it work locally but fail in production?

Local static servers often omit the same header constraints as production CDNs, or you test with a different base URL. Always verify crossOriginIsolated on the public URL.

Can I fix this only in index.html meta tags?

No. These policies must be delivered as HTTP response headers from the server or edge configuration.