skip to content

What does adding the sandbox attribute to an HTML <iframe> do, and how do tokens such as allow-scripts and allow-forms change that?

level: middleimportance: must knowfreq 70%

answer

  1. deny first, then grant back
  2. empty value is the strictest state
  3. no cookies, no localStorage inside
  4. one token per capability, no wildcard
  5. the pairing that undoes itself

basics

~20 s

The 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

for a junior

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.

for a middle

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.

for a senior

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.

for a principal

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

context