Why your web game won't load in an iframe (and how to fix it)
Short answer
Your server is telling browsers not to show the page inside other sites. The usual cause is an X-Frame-Options header or a Content-Security-Policy: frame-ancestors rule. Remove X-Frame-Options and allow framing with Content-Security-Policy: frame-ancestors *. Also make sure the game is served over https and doesn't redirect itself out of the frame.
Sites like Joystumble, itch.io jams, and game portals show your game inside their own page using an <iframe>. When the frame shows a blank page, a grey icon, or "refused to connect", the browser blocked it on purpose, because your game's server asked it to.
Find out what's blocking it
Open your browser's developer tools on the page that embeds your game and look at the console. You'll usually see one of these:
Refused to display '…' in a frame because it set 'X-Frame-Options' to 'sameorigin'.Refused to frame '…' because an ancestor violates the following Content Security Policy directive: "frame-ancestors 'self'".Mixed Content: The page at 'https://…' was loaded over HTTPS, but requested an insecure frame 'http://…'.
You can also check your headers from a terminal:
curl -sI https://yourgame.example | grep -iE "x-frame-options|content-security-policy"
No output means neither header is set, and headers aren't your problem.
The five usual causes
1. X-Frame-Options
An older header. DENY blocks all framing and SAMEORIGIN allows only your own site. There's no value that means "allow everyone", so the fix is to remove it. Security middleware such as Express's helmet adds it by default.
2. CSP frame-ancestors
The modern replacement. frame-ancestors 'self' or a list of domains blocks everyone else. To allow any site, use:
Content-Security-Policy: frame-ancestors *
If you'd rather allow only specific sites, list them: frame-ancestors 'self' https://joystumble.com https://*.itch.io.
3. http instead of https
A page served over https can't frame an http page. Browsers block it as mixed content. Serve your game over https. Every free host in our hosting guide does this automatically. Also watch for redirects: if https://yourgame.com redirects to an http:// address, it fails too.
4. Redirects and frame-busting scripts
Some pages redirect with JavaScript to another domain that blocks framing, or contain code like this:
if (window.top !== window.self) window.top.location = window.location.href;
That's a "frame buster". It tries to break out of any frame. Remove it. Sandboxed iframes usually stop it from working anyway, and then the game just doesn't load.
5. Login and cookies
Browsers now isolate cookies and storage for pages inside iframes. A game that needs you to sign in, or that expects to share saved progress with your main site, may act logged-out inside a frame. Let people play without an account, and keep saves in the game's own localStorage. Inside a frame, that storage is kept separate from direct visits, but it works.
Copy-paste fixes by host
Netlify and Cloudflare (a _headers file)
Create a file named _headers next to your index.html:
/*
Content-Security-Policy: frame-ancestors *
Content-Type: text/html; charset=utf-8
Only include the Content-Type line if your site is a single HTML page, since it applies to every path. If an X-Frame-Options header still appears, find where it's being added (a plugin, framework, or dashboard setting) and remove it.
Vercel (vercel.json)
{
"headers": [
{ "source": "/(.*)", "headers": [
{ "key": "Content-Security-Policy", "value": "frame-ancestors *" }
] }
]
}
Express with helmet
app.use(helmet({
frameguard: false, // stops X-Frame-Options
contentSecurityPolicy: { directives: { frameAncestors: ["*"] } },
}));
nginx
Delete any add_header X-Frame-Options …; line, then add:
add_header Content-Security-Policy "frame-ancestors *" always;
GitHub Pages
GitHub Pages doesn't send either header by default, so games hosted there usually embed fine. You can't add custom headers there, so if you need them, use another host.
Make your game behave well in a frame
- Fill the frame. Size your canvas to the window (
100vw×100vh) and handle resize events. Frames come in every shape. - Start sound on a click. Browsers block audio until the player interacts, inside frames and out.
- Grab keyboard focus when clicked. Keys only reach your game once the frame has focus, so a "click to start" screen helps.
- Don't open popups on load. Embedding sites often block them.
Test it yourself
Save this as test.html, open it in a browser, and see whether your game appears:
<iframe src="https://yourgame.example" style="width:100%;height:90vh;border:0"
allow="fullscreen; autoplay; gamepad"></iframe>
When you submit to Joystumble, we run this check automatically and tell you exactly what's blocking your game. If you'd rather skip all of this, upload your game as a single HTML file and we'll host it in a way that always embeds.
FAQ
How do I allow my website to be embedded in an iframe?
Remove the X-Frame-Options header and send Content-Security-Policy: frame-ancestors * (or a list of sites you allow). Also serve the page over https.
What does "refused to connect" in an iframe mean?
The embedded site sent a header (X-Frame-Options or CSP frame-ancestors) telling the browser not to show it inside other pages. Only the site's owner can change that.
Is it safe to allow my game to be framed?
For a game without logins or sensitive actions, yes. The risk framing protects against, clickjacking, matters for pages where a hidden click could do something harmful, like change account settings or make a payment.
Can I set X-Frame-Options to allow all sites?
No. X-Frame-Options only supports DENY and SAMEORIGIN (ALLOW-FROM is obsolete). Remove the header and use CSP frame-ancestors instead.