Checked on 2026-08-24.
sandbox is the attribute on <iframe> that takes capabilities away from the framed page. Whether that page loads at all is decided by the other site's response headers, and you've no say in it. This part is yours.
It also works backwards from how attributes usually read. Writing sandbox with no value doesn't switch a feature on — it takes almost everything away. Each token you add gives one capability back. If you've ever written sandbox="allow-scripts" and been surprised that forms stopped submitting, that's the reason: you didn't enable scripts, you disabled everything and re-enabled one thing.
An empty sandbox
The HTML Standard is compact about what happens with no tokens at all. When the attribute is set, "the content is treated as being from a unique opaque origin, forms, scripts, and various potentially annoying APIs are disabled, and links are prevented from targeting other navigables."
Unpack those four clauses, because each one has consequences.
A unique opaque origin means the page isn't same-origin with anything, including itself in another frame. The flag that does this — the sandboxed origin browsing context flag — "prevents script from reading from or writing to the document.cookie IDL attribute, and blocks access to localStorage." A page in an empty sandbox has no cookies and no local storage, whatever the server sent.
Scripts disabled means no JavaScript executes at all. A second flag comes along with it: the sandboxed automatic features flag, which "blocks features that trigger automatically, such as automatically playing a video or automatically focusing a form control." The two share one token, and the specification says why — "when scripts are enabled these features are trivially possible anyway, and it would be unfortunate to force authors to use script to do them when sandboxed rather than allowing them to use the declarative features."
Forms disabled means form submission is blocked. The page renders its form; the submit does nothing.
Links cannot target other navigables is the navigation half. The sandboxed navigation flag stops the content navigating any browsing context but itself and those nested inside it; the auxiliary navigation flag stops it opening windows; and two more stop it navigating or closing the top-level browsing context, one consulted with transient user activation and one without.
Between the scripts flag and that navigation group, an empty sandbox is a reliable way to neutralize a frame-buster script — which is exactly why it appears as an attack in clickjacking explained. A defense you own is also a weapon someone else owns.
The tokens
The HTML Standard lists thirteen allowed values for the attribute. One more, allow-storage-access-by-user-activation, is defined by the Storage Access API specification, which patches HTML's parsing algorithm rather than living in the HTML Standard's own list — worth knowing if you go hunting for it in the wrong document.
| Token |
What it restores |
allow-scripts |
Script execution, and the automatic features that come with it |
allow-same-origin |
The content's real origin, in place of the opaque one |
allow-forms |
Form submission |
allow-popups |
Creating new auxiliary browsing contexts, by target or window.open() |
allow-popups-to-escape-sandbox |
Stops the sandbox propagating into those popups |
allow-modals |
alert(), confirm(), print(), prompt(), and the beforeunload dialog |
allow-downloads |
Downloads, whether by link or by a navigation handled as one |
allow-top-navigation |
Navigating and closing the top-level browsing context |
allow-top-navigation-by-user-activation |
The same, only with transient activation |
allow-top-navigation-to-custom-protocols |
Handing non-fetch schemes off to external software |
allow-presentation |
The Presentation API |
allow-orientation-lock |
Locking the screen orientation |
allow-pointer-lock |
The Pointer Lock API |
allow-storage-access-by-user-activation |
The right to ask for storage access, via the Storage Access API |
Two notes on that table.
allow-storage-access-by-user-activation grants permission to make a request, not the storage itself. The framed page still has to call requestStorageAccess(), and may still be refused. It usually needs a user gesture too, though not always: the algorithm short-circuits to a grant for a top-level document, for an embed same-site with the top-level site, and for a permission already decided, all before it reaches the activation check. It's also useless alone: MDN pairs it with allow-scripts and allow-same-origin, because the page needs script to make the call and a real origin to have cookies in. Cookies inside an iframe covers what happens after the grant, including the fact that it does not override SameSite.
And one flag has no token at all. The sandboxed document.domain flag is added unconditionally by the parsing algorithm, with no keyword that removes it. In a sandbox, the document.domain setter is off, full stop.
Two conformance rules people miss
The specification calls out two token combinations as document conformance errors — not errors the browser reports, just markup that doesn't mean what it looks like.
allow-top-navigation and allow-top-navigation-by-user-activation "must not both be specified, as doing so is redundant; only allow-top-navigation will have an effect in such non-conformant markup." If you wrote both hoping to be careful, you've written the permissive one.
allow-top-navigation-to-custom-protocols "must not be specified if either allow-top-navigation or allow-popups are specified," for the same reason: either of those already lifts the custom-protocols flag.
The two combinations that undo the sandbox
allow-scripts with allow-same-origin. The specification's warning is worth quoting exactly: "Setting both the allow-scripts and allow-same-origin keywords together when the embedded page has the same origin as the page containing the iframe allows the embedded page to simply remove the sandbox attribute and then reload itself, effectively breaking out of the sandbox altogether."
Read the condition carefully, because it's narrower than the version that circulates. The escape needs the framed page to be same-origin with the container. Given that, allow-same-origin restores the real origin, allow-scripts restores script, and the framed page can reach into the parent document, delete the attribute, and reload itself unrestricted.
The specification's own answer isn't a cleverer token list. It says potentially hostile files "should not be served from the same server as the file containing the iframe element," and that content "should be served from a separate dedicated domain" — which also protects you if someone is tricked into opening the hostile page directly, outside any frame.
allow-popups with allow-popups-to-escape-sandbox. This one's quieter. The parsing algorithm sets the "sandbox propagates to auxiliary browsing contexts" flag unless allow-popups-to-escape-sandbox is present, and that flag "prevents content from escaping the sandbox by ensuring that any auxiliary browsing context it creates inherits the content's active sandboxing flag set."
Take it away and the inheritance stops. Add allow-popups so a window can be created at all, and the framed page has a route to a full-privilege browsing context. The frame is still sandboxed; the window it opens isn't. There are legitimate uses — an OAuth popup is the obvious one — but the pairing deserves a comment in your code, because nothing about it looks dangerous at a glance.
What a sandbox does not do
It does not grant a framing permission. This is the commonest misreading, and the direction of the control explains it. sandbox is an attribute on your element; it restricts what the framed page may do once it's loaded. Whether the browser loads it at all is decided by that page's own X-Frame-Options or frame-ancestors, which arrive with its response. No attribute of yours changes them — see X-Frame-Options explained and the frame-ancestors directive, or the measured headers for 60 apps at can I embed this site in an iframe?.
It is not a network filter. Two flags do stop specific network operations — forms can't submit and downloads can't start — but nothing in the flag set governs what the page may load. The document is still fetched, and it still pulls its own images, stylesheets, and fonts. An empty sandbox removes script, so script-initiated requests never happen, but that's a side effect of the scripts flag. To restrict where a page loads resources from, use a Content Security Policy.
It does not survive an edit after load. The specification is unambiguous: "These flags only take effect when the content navigable of the iframe element is navigated. Removing them, or removing the entire sandbox attribute, has no effect on an already-loaded page." Tightening a sandbox on a live frame does nothing until the frame navigates.
It does not weaken as you nest it. The HTML Standard works an example: page A frames B with allow-same-origin allow-forms, and B frames C with allow-scripts. C ends up with every restriction. Scripts are off because A's frame disabled them, "and this overrides the allow-scripts keyword set on the iframe in B." Forms are off because B's inner frame didn't carry allow-forms. Restrictions accumulate down the chain; permissions don't.
The CSP version
There's a second way to deliver the same thing. CSP has a sandbox directive that "specifies an HTML sandbox policy which the user agent will apply to a resource, just as though it had been included in an iframe with a sandbox property," and its tokens must be the same keywords HTML defines for the attribute.
The difference is who sends it: the directive travels on the resource's own response, so a page uses it to sandbox itself. Two limits come with that. The directive "has no reporting requirements" and "will be ignored entirely when delivered in a Content-Security-Policy-Report-Only header, or within a meta element" — so you can't trial it in report-only mode, and you can't set it from inside the HTML.
Picking tokens
Start empty and add only what breaks. That's not a slogan; it's the only order that works, because the attribute can't tell you which restriction caused a symptom.
A snippet of untrusted HTML needs nothing. A widget that talks to its own server usually needs allow-scripts and, if it stores anything, allow-same-origin — served from a domain you don't otherwise use. Anything with a form needs allow-forms. And if you reach for allow-top-navigation, ask what the framed content is for, because it wants to move the page out from under you.
Questions
Will sandbox make an unframeable site load?
No, and it's worth being blunt, because that's the hope that sends people here. The sandbox attribute only adds restrictions. A site answering with X-Frame-Options: DENY, or a frame-ancestors list that leaves you out, is refused before your attribute is ever consulted. Nothing on your side of the frame grants a permission the other side withheld.
Is allow-scripts allow-same-origin always unsafe?
Not always — it's unsafe in the specific case the specification names, where the framed page is same-origin with the page containing the iframe. Framing genuinely third-party content with both tokens doesn't hand it your document, and that combination is what the Storage Access API requires. The rule to follow is the specification's structural one: serve anything you do not fully trust from a separate dedicated domain, and the dangerous case cannot arise.
Does a sandbox stop the framed page reading my page?
An opaque origin can't reach into your document, and neither can a cross-origin page without a sandbox — that's the same-origin policy doing its usual job. What allow-same-origin changes is only whether the framed page gets its own real origin back. It never gives it yours. The case to watch is the one above, where the framed content already shares your origin, so restoring its real origin restores access to you.