On this page
- What the iframe sandbox attribute blocks by default
- iframe sandbox tokens and what each one allows
- Why allow-scripts plus allow-same-origin breaks the sandbox
- Which sandbox string to use for three cases
- Can you sandbox a frame with a header
- How to test iframe sandbox tokens in the browser
- Why a sandboxed embed renders as a blank frame
What the iframe sandbox attribute blocks by default
Add the bare attribute and the browser applies every restriction it has. An iframe sandbox with no value is the strictest setting, not the loosest one.
<iframe src="/preview.html" sandbox></iframe>Inside that frame the browser switches off, all at once:
- Scripts: script tags, inline event handlers and javascript: URLs are all inert.
- Forms: a submit button does nothing.
- Popups: window.open and links with target="_blank" are ignored.
- Plugins: embedded plugin content refuses to load.
- Top-level navigation: the frame cannot move the page around it.
- Downloads, modal dialogs such as alert and confirm, pointer lock, and automatic features like autofocus.
- A usable origin: the document gets an opaque origin, so cookies, localStorage, sessionStorage and IndexedDB are unreachable and outgoing requests carry a null Origin header.
That last one catches people out. The framed document is no longer on your origin, and it is not on its own origin either. It is nowhere, which is exactly why it cannot reach into anything.
From there the attribute works by addition. Every token you list hands back one capability and nothing else, and tokens the browser does not recognise are ignored, so a typo quietly leaves the capability switched off. The MDN reference for the iframe element tracks the current list.
iframe sandbox tokens and what each one allows
Here is the working set, with the symptom you get when the token is missing.
| Token | What it gives back | What breaks without it |
|---|---|---|
| allow-scripts | JavaScript execution | Nothing in the frame runs or reacts |
| allow-same-origin | The document keeps its real origin | Cookies, storage and same-origin requests fail |
| allow-forms | Form submission | Submitting silently does nothing |
| allow-popups | window.open and target="_blank" | New windows and tabs never open |
| allow-popups-to-escape-sandbox | Popups open without the sandbox | A login popup inherits every restriction |
| allow-modals | alert, confirm, prompt, print | Those calls return without showing anything |
| allow-top-navigation | The frame can navigate the top page | Links with target="_top" do nothing |
| allow-top-navigation-by-user-activation | The same, but only after a real click | Click-driven checkout redirects stall |
| allow-top-navigation-to-custom-protocols | Links to schemes such as mailto: | Those links are dropped |
| allow-downloads | Downloads started inside the frame | The download is cancelled before it starts |
| allow-pointer-lock | The Pointer Lock API | Mouse-look controls never engage |
| allow-storage-access-by-user-activation | Requesting storage access after a click | requestStorageAccess is refused outright |
Rarer tokens exist for screen orientation locking and the Presentation API, and support for the newest ones varies, so check before relying on them.
What allow-scripts gives the framed page
This is the token most embeds need and the one people fear most. On its own it is far less dangerous than it looks: scripts run, but they run in a document with an opaque origin. They cannot read your cookies, they cannot touch your DOM, and any fetch they make is cross-origin. A hostile snippet can wreck its own little box and not much else.
What iframe allow same origin gives back
The iframe allow same origin token stops the browser from replacing the document's origin with an opaque one. The frame keeps the origin it was actually served from, which is what makes cookies, localStorage and same-origin API calls work again.
Read that carefully: it restores the frame's own origin, not yours. For a widget served from another domain, that origin is still foreign to your page and the same-origin policy keeps it out. The token only becomes a problem when the frame and the embedding page share an origin.
Which tokens control forms, popups and dialogs
allow-forms, allow-popups and allow-modals produce the most confusing bug reports, because nothing errors. The user clicks submit, or a widget calls window.open, or a script calls confirm, and the browser simply declines. There is no exception to catch and often no visible feedback.
allow-popups has a companion. A window opened from a sandboxed frame inherits the same restrictions, so an OAuth popup opens and then does nothing useful. allow-popups-to-escape-sandbox lifts the restrictions from the popup while the frame stays sandboxed.
What allow-downloads and the navigation tokens do
allow-downloads covers any download the frame starts, from a link or from script; without it the browser cancels the transfer. For navigation, prefer allow-top-navigation-by-user-activation over plain allow-top-navigation: both let the frame replace the page the user is looking at, but the first demands a real user gesture, which rules out a silent redirect the moment the frame loads.
Why allow-scripts plus allow-same-origin breaks the sandbox
Listing both tokens on a frame whose content is same-origin with the surrounding page removes the protection entirely. The framed document has scripts and it has your origin, so it can reach through window.parent, find its own iframe element, and edit it.
// running inside the "sandboxed" frame, same origin as the parent
const frameEl = window.parent.document.querySelector('#untrusted');
frameEl.removeAttribute('sandbox');
window.location.reload(); // comes back with no restrictions at allThe combination is fine, and usually required, for a third-party embed on a different domain. It is only a self-escape when the frame and the page share an origin, a case the HTML specification calls out directly.
Which sandbox string to use for three cases
Three situations cover most real work. Start here and remove whatever the frame turns out not to need.
Untrusted snippet from a user
sandbox="allow-scripts"For HTML someone pasted into your app. Scripts run so the snippet behaves normally, but the opaque origin keeps it away from cookies, storage and your DOM. Drop to the bare attribute when the snippet is markup only and nothing needs to run.
Third-party embed such as a map or player
sandbox="allow-scripts allow-same-origin allow-popups allow-popups-to-escape-sandbox"The widget lives on another domain, so allow-same-origin restores its origin and not yours. Add allow-forms only if the embed has a form, and allow-downloads only if it offers a file.
Payment or authentication frame
sandbox="allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox allow-top-navigation-by-user-activation"These frames carry a session, submit a form, and often hand off to a bank page for a step-up challenge. The user-activation variant lets that handoff happen on a click while blocking silent redirects. Check the provider's documentation too: many publish the exact set they support.
Can you sandbox a frame with a header
Yes. Content Security Policy has a sandbox directive that takes the same token list and applies it to the response itself, wherever that response is framed. The attribute is set by whoever embeds the document; the header is set by whoever serves it. If you are already writing policies, our guide to Content Security Policy headers covers the other directives.
Two neighbours get mistaken for sandbox. frame-ancestors decides who may frame your page at all, and the allow attribute, backed by Permissions Policy, decides whether a frame may use the camera, microphone or geolocation. Sandbox tokens have no say over either.
Restrictions also flow downhill. A frame nested inside a sandboxed frame inherits every flag, and its own sandbox attribute can only take more away, never add anything back.
How to test iframe sandbox tokens in the browser
Reading the table tells you what should happen. Watching a call fail in front of you is what makes it stick, and it takes a minute in the HTML/CSS/JS playground. Everything runs in your browser and nothing is uploaded.
Put a sandboxed frame in the HTML tab:
<iframe id="box" sandbox srcdoc="<p>Framed content</p>"></iframe>Then probe it from the JavaScript tab:
const box = document.getElementById('box');
try {
console.log('document reachable:', !!box.contentDocument);
} catch (err) {
console.error('blocked:', err.message);
}Open the console panel with the terminal button next to Live Preview. With the bare sandbox attribute the frame has an opaque origin and the line reads false. Change it to sandbox="allow-same-origin", wait for the preview to redraw, and the same line reads true. That one flip is the whole model in miniature.
The preview updates about a second after you stop typing, the refresh button re-renders immediately and clears collected messages, and the trash button empties the panel. Your code is kept in browser storage between visits, and the download button packages the editors as a ZIP.
Try one token flip livePaste the frame, change one token, read the consoleWhy a sandboxed embed renders as a blank frame
A blank or dead frame almost always comes from one of four things.
The embed needs scripts and allow-scripts is missing, so its bootstrap code never runs. Or it needs cookies and storage and allow-same-origin is missing, so its session lookup throws a SecurityError. Or the provider refuses to be framed at all through frame-ancestors or X-Frame-Options, which no token can override. Or a token is misspelled and quietly discarded.
The browser console names the first two clearly, usually with a blocked-by-sandbox message that mentions the missing token. Add that one token and test again rather than pasting in the full list. If the embed snippet arrives as one unreadable line, run it through the HTML beautifier first to see what it asks for.
Work token by token and the attribute stops feeling like a lottery. You are handing back one capability at a time, and you can see the effect of each one.

Stop guessing which token your embed needs
Paste a sandboxed iframe into the HTML tab, probe it from the JavaScript tab, and watch the console panel report what the browser refused.
Frequently asked questions
Yes. Writing sandbox, or sandbox="", applies every restriction at once: no scripts, no forms, no popups, no plugins, no top-level navigation, no downloads and an opaque origin instead of a real one. It is the most restrictive setting available, and you add tokens from there until the frame does only what it genuinely needs.
Generally yes, and that pairing is the recommended one for untrusted code. Scripts run inside a document with an opaque origin, so they cannot read your cookies, reach your DOM or use storage. They can only affect their own frame, which is exactly what sandboxing is meant to achieve.
No. Sandboxing restricts capabilities, not loading. A sandboxed frame can still fetch resources and call APIs, although requests from an opaque origin carry a null Origin header and many servers reject them. To control which hosts a document may contact, use Content Security Policy directives instead of sandbox tokens.
Yes. Sandbox flags flow into every frame nested inside a sandboxed frame. A child frame can add restrictions with its own sandbox attribute, but it can never give back a capability the parent removed, so you cannot widen permissions by nesting another iframe inside a restricted one.
No. Those are governed by Permissions Policy through the iframe's allow attribute, a separate mechanism. Sandbox decides what a frame may do with scripts, forms, popups, dialogs, navigation and its origin, while the allow attribute decides which powerful device features the frame is even permitted to request.