What does adding the sandbox attribute to an HTML <iframe> do, and how do tokens such as allow-scripts and allow-forms change that?
answer
- deny first, then grant back
- empty value is the strictest state
- no cookies, no localStorage inside
- one token per capability, no wildcard
- the pairing that undoes itself
basics
~20 sThe sandbox attribute revokes an iframe's capabilities by default: scripts, form submission, popups, top-level navigation, downloads and modal dialogs are blocked, and the frame runs in a unique opaque origin. Each allow-* token grants one capability back.
solid answer
~40 s`sandbox` is a deny-by-default switch on `<iframe>`. Writing `sandbox` or `sandbox=""` applies every restriction at once: scripts do not execute, forms do not submit, `window.open` and `target="_blank"` do nothing, the frame cannot navigate the top-level page, downloads and `alert`/`confirm`/`print` are blocked, and — importantly — the document is forced into a unique opaque origin, so it has no cookies, no `localStorage`, and is cross-origin to everything including the server it came from. You then hand capabilities back one token at a time in a space-separated list: `sandbox="allow-scripts allow-forms"`. Omitting the attribute entirely is the opposite of an empty value — an unsandboxed frame keeps all of those powers. The rule of thumb is to start from `sandbox=""`, add the single token that fixes the broken behaviour, and never reflexively add `allow-same-origin` alongside `allow-scripts`.
go deeper
Know that sandbox on an <iframe> takes capabilities away rather than adding them, and that a bare sandbox or sandbox="" is the most restrictive form. Be able to name allow-scripts and allow-forms as tokens that hand something back.
Explain the deny-by-default model precisely: list what a full sandbox removes, and say that the document is pushed into a unique opaque origin so cookies and localStorage are gone. Show that you know tokens are additive and space-separated.
Show the operating judgment: start from the empty value, add tokens one at a time against observed breakage, prefer allow-top-navigation-by-user-activation, and treat allow-same-origin as the token that needs an origin-separation story before you grant it.
Own the policy question of hosting untrusted embeds at all — whether third-party widgets live on a separate origin so that sandbox tokens stay meaningful, how capability grants get reviewed, and where markup-level containment stops and response-level controls have to take over.
## The shape of the attribute `sandbox` is a *space-separated token list* attribute on `<iframe>`. Three states matter and they are easy to confuse: ```html <!-- 1. no sandbox: the frame has every capability a normal document has --> <iframe src="https://widget.example/embed"></iframe> <!-- 2. empty value (or the bare attribute): every restriction applied --> <iframe sandbox src="https://widget.example/embed"></iframe> <iframe sandbox="" src="https://widget.example/embed"></iframe> <!-- 3. selective: restrictions applied, then two of them lifted --> <iframe sandbox="allow-scripts allow-forms" src="https://widget.example/embed"></iframe> ``` The counter-intuitive part is that the *empty* value is the strictest configuration and the *absent* attribute is the loosest. Candidates who read `sandbox=""` as "an empty sandbox, so nothing is sandboxed" have it exactly backwards. ## What the full sandbox actually removes With no tokens, the browser removes, among other things: - **Script execution.** Inline and external scripts in the framed document do not run at all. The document still parses and renders, which is why people mistakenly believe scripts are running. - **Form submission.** A `<form>` inside the frame will not submit. - **Popups.** `window.open()` returns `null` and `target="_blank"` links do nothing. - **Top-level navigation.** The frame cannot point the containing browser tab somewhere else — the classic "an ad in a frame hijacked the whole page" attack. - **Downloads**, **pointer lock**, **fullscreen**, **screen orientation lock**, and **modal dialogs** (`alert`, `confirm`, `prompt`, `print`). - **Automatic playback** of media that would otherwise autoplay. ## The opaque origin — the piece people miss A sandboxed document is placed in a **unique opaque origin**: an origin that matches nothing, not even another copy of itself. The practical consequences are larger than the capability list: - `document.cookie` yields nothing useful and cannot set cookies. - `localStorage` and `sessionStorage` access throws a `SecurityError`. - Requests the frame makes to its own server are *cross-origin*, so they arrive without cookies and are subject to CORS. - The frame is cross-origin to the embedding page, so neither can touch the other's DOM. That last point is what makes `sandbox` a containment tool rather than a cosmetic one, and it is why `allow-same-origin` — the token that restores the document's real origin — is the token to think hardest about. ## The token vocabulary The tokens that come up in practice are `allow-scripts`, `allow-forms`, `allow-same-origin`, `allow-popups`, `allow-popups-to-escape-sandbox`, `allow-modals`, `allow-downloads`, `allow-pointer-lock`, `allow-presentation`, `allow-orientation-lock`, `allow-top-navigation`, and `allow-top-navigation-by-user-activation`. Each grants exactly one thing back; there is no wildcard token and no way to express "everything except X" other than by listing what you want. Two of these deserve a caveat in an interview answer. `allow-top-navigation` lets the frame navigate the whole tab unconditionally; `allow-top-navigation-by-user-activation` is the safer variant that only permits it as the direct result of a user gesture — prefer it. And `allow-same-origin` combined with `allow-scripts`, when the framed content comes from your own origin, effectively cancels the sandbox, because the framed script becomes same-origin with the embedder and can reach out and delete the `sandbox` attribute from its own frame element. ## How this differs from neighbouring attributes `sandbox` is not the only restricting attribute on `<iframe>`, and mixing them up is a common stumble. `allow` is a separate attribute governing delegation of powerful features (camera, microphone, geolocation, payment) to the frame; `referrerpolicy` governs what referrer information the frame's requests carry; `loading="lazy"` is a fetch-timing hint. None of them substitute for `sandbox`, and `sandbox` does not substitute for them: a fully sandboxed frame is still a frame that made a network request to a third party. One more boundary worth stating plainly: `sandbox` is a control the **embedder** applies to content it embeds. It gives the embedded site no protection whatsoever, and it does nothing to stop *your* page from being embedded by someone else — that is a decision the framed site expresses in its HTTP response, not something you can assert from markup. ## Practical discipline Start at `sandbox=""`, load the embed, see what breaks, and add the single token that fixes it. Write the tokens in the markup rather than adding them from script, because a reader of the HTML should be able to see the frame's full capability set in one place. And give every `<iframe>` a `title` while you are there — assistive technology announces the frame by that string, and "iframe" is what a user hears without it.
- If I omit allow-same-origin, what breaks for a frame that legitimately needs its own login session?Everything session-shaped. The document sits in an opaque origin, so `document.cookie` is unusable, `localStorage` throws, and its own fetches to its own server go out cross-origin without cookies. A frame that must read its own session state needs `allow-same-origin` — which is fine when the frame is served from a genuinely different origin than the embedder, and dangerous when it is not.
- Does sandbox protect the embedded site from the page embedding it?No — it runs entirely in the embedder's favour. The embedder chooses the tokens and can simply omit the attribute. A site that wants to control whether it can be framed at all has to say so in its own HTTP response; there is no markup a page can write to defend itself from being embedded.
- Why is allow-top-navigation-by-user-activation usually the better choice than allow-top-navigation?Both let the frame navigate the top-level tab, but the user-activation variant only permits it as the direct consequence of a real user gesture such as a click. That kills the drive-by case where a third-party embed silently redirects the whole page while the user is reading it, while still letting a deliberate "open this in the main window" link work.
saying these in an interview costs you the question
- Reads sandbox="" as meaning no restrictions are applied
- Thinks a sandboxed frame protects the embedded site from the parent
- Assumes scripts run because the framed page still renders
- Adds allow-same-origin reflexively whenever an embed misbehaves
- Believes sandbox blocks the frame's network requests entirely
- Thinks sandbox stops your own page from being framed elsewhere