Command Palette

Search for a command to run...

Hectal
PHASE 5Intermediate ~26 min· topic 5 of 7

Topic 5.5

Iframes and Embeds

In one line

An <iframe> shows another web page inside yours, such as a map or a video; title, sandbox, allow and referrerpolicy make it accessible and safe, and server headers stop other sites from framing yours.

Think of it like this

A window in your house that looks into the neighbour's shop. You can see their shop and even talk through the window, but it's still their shop: you can't rearrange their shelves, and they can't walk into your kitchen. An <iframe> is that window. And just as you'd put a lock on a window, sandbox decides what the page behind it may do.

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

iframe
An element that shows another complete web page inside a rectangle on your page.
Embed
A piece of another service (a map, video, post) shown inside your page, usually with an iframe.
Origin
The protocol, domain and port of a page, like https://hectal.in. Pages from different origins are kept apart.
Same-origin policy
The browser rule that scripts from one origin can't read pages from another origin.
sandbox
An iframe attribute that turns off risky abilities (scripts, forms, popups) unless you allow them back.
Permissions Policy
Rules saying which browser features (camera, fullscreen, autoplay) a page or frame may use. Set on iframes with allow.
Referrer
The address of the page you came from, sent to the next site in the Referer header.
Clickjacking
Tricking someone into clicking something on a hidden, framed copy of a site.
postMessage
A safe way for a page and an iframe from different origins to send each other messages.

Step by step

01Embed a map

Map services give you an embed snippet (in Google Maps: Share → Embed a map). Paste it, then fix what they leave out: a meaningful title, lazy loading and a responsive size.

index.htmlwhole filehtml
<iframe src="https://www.google.com/maps/embed?pb=…"
        title="Map showing our office in Pune"
        width="600" height="450" style="border:0"
        loading="lazy" referrerpolicy="no-referrer-when-downgrade"
        allowfullscreen></iframe>

02Embed a video

Video sites give a snippet with an allow list. Each item is a feature the player needs: autoplay, encrypted-media (protected video), picture-in-picture, and fullscreen through allowfullscreen. Keep the referrerpolicy they give you: some players need to know which site is embedding them.

index.htmlwhole filehtml
<iframe width="560" height="315"
        src="https://www.youtube-nocookie.com/embed/VIDEO_ID"
        title="How the browser renders a page (video)"
        loading="lazy"
        allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
        referrerpolicy="strict-origin-when-cross-origin"
        allowfullscreen></iframe>

03Make it responsive

The width and height attributes give the shape. In CSS, make the frame fill its container and keep the shape with aspect-ratio.

style.csswhole filecss
iframe {
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 9;
  border: 0;
}

04Sandbox content you don't trust

For user-written HTML or a third-party widget, start from an empty sandbox and add only what's needed. Here the widget may run scripts and open links in new tabs, but can't submit forms or navigate your page.

index.htmlwhole filehtml
<iframe src="https://widgets.example.com/poll/42"
        title="Reader poll"
        sandbox="allow-scripts allow-popups"
        width="400" height="300"></iframe>
Sandbox content you don't trustdiagram
Rendering diagram…

05Talk across the frame with postMessage

The framed page sends a message; your page listens and checks who sent it (event.origin) before trusting it.

app.jswhole filejavascript
window.addEventListener("message", (event) => {
  if (event.origin !== "https://widgets.example.com") return; // ignore strangers
  console.log("Poll result:", event.data);
});

06Protect your own site from being framed

Set these response headers on your server (or hosting config). Test it: make a page on another origin that frames your site. The browser should refuse and print an error in the console.

HTTP response headerswhole filehttp
Content-Security-Policy: frame-ancestors 'self'
X-Frame-Options: SAMEORIGIN
Protect your own site from being frameddiagram
Rendering diagram…

Try it yourself

  1. 1

    Unlock frame B

    In the first example, predict what changes if you write sandbox="allow-scripts" on frame B. Run it: now both messages arrive.

  2. 2

    Remove the sandbox

    In the second example, delete the whole sandbox attribute and run it. Predict the last line. (Here the preview itself is sandboxed, so the frame stays separate; on a normal page a srcdoc frame without sandbox shares the parent's origin and the answer would be true.)

  3. 3

    Check a site's framing rules

    Open any site, DevTools → Network, click the first (document) request and look at the Response Headers for content-security-policy with frame-ancestors, or x-frame-options. Which of your favourite sites can be framed?

Code & diagrams

Sandbox in action: scripts allowed vs blocked New tab

Both frames contain the same kind of script. Only the one with `allow-scripts` can run it.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

Message: Hello from frame A
Done waiting. Frame B never spoke.
The safety attributes, read back New tab

The sandboxed frame gets an opaque origin, so even the page that made it can't read inside: the same-origin policy at work.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

title: Reader poll
sandbox tokens: [ 'allow-scripts', 'allow-popups' ]
allows forms? false
allow: fullscreen; camera 'none'
referrerPolicy: no-referrer | loading: lazy
Can this page read the frame's document? false
Real embeds (they need the internet, so they're not live here)html
<!-- A map: give it a real title and lazy-load it -->
<iframe src="https://www.openstreetmap.org/export/embed.html?bbox=73.84,18.50,73.87,18.53&amp;layer=mapnik"
        title="Map of central Pune" width="600" height="400" loading="lazy" style="border:0"></iframe>

<!-- A video player -->
<iframe src="https://www.youtube-nocookie.com/embed/VIDEO_ID"
        title="Course introduction video" width="560" height="315" loading="lazy"
        allow="autoplay; encrypted-media; picture-in-picture" allowfullscreen
        referrerpolicy="strict-origin-when-cross-origin"></iframe>

Break it on purpose

Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.

Break #1

Frame a site that forbids it

Write <iframe src="https://www.google.com" title="Google"></iframe> on your page.

terminal
$ Console
── what you'll see ──
Refused to display 'https://www.google.com/' in a frame because it set 'X-Frame-Options' to 'sameorigin'.

Break #2

Forget the title

Embed a video with no title attribute.

terminal
$ Lighthouse → Accessibility
── what you'll see ──
`<frame>` or `<iframe>` elements do not have a title

Myth vs fact

Myth

Iframes are old and should never be used.

Fact

Framesets (<frameset>) are obsolete. Iframes are the standard, secure way to embed maps, video players, payment fields and untrusted content.

Myth

My JavaScript can read anything inside an iframe on my page.

Fact

Only if the frame has the same origin as your page. Cross-origin frames are sealed off by the same-origin policy; use postMessage to communicate.

Myth

sandbox="allow-scripts allow-same-origin" is safe for my own pages.

Fact

For same-origin content, that pair lets the framed script remove its own sandbox. Browsers even warn about it in the console.

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    Each cross-origin iframe in Chromium may run in its own renderer process (Site Isolation), which protects against Spectre-style memory attacks but costs memory. Many embeds on one page add up quickly.

  • ▸

    Heavy third-party embeds (a YouTube iframe pulls in several hundred kilobytes of JavaScript) are often replaced with a facade: a static thumbnail with a play button that swaps in the real iframe on click.

  • ▸

    loading="lazy" on iframes reached all major browsers in 2023 (Baseline 2023). Like images, don't lazy-load a frame in the first viewport.

  • ▸

    frame-ancestors in CSP replaces X-Frame-Options and supports lists of allowed origins (frame-ancestors 'self' https://partner.example). When both are present, browsers that support CSP use frame-ancestors. Note it only works as a header, not in a <meta> tag.

Remember this

  1. 1

    An iframe (inline frame) is an element that loads a whole separate web page in a rectangle of your page: <iframe src="https://www.example.com/widget" title="Weather widget" width="400" height="300"></iframe>. The framed page has its own HTML, CSS, JavaScript and history. Video players, maps, payment forms, social posts and ads are usually iframes. **srcdoc** puts the HTML of the inner page directly in an attribute instead of loading a URL (Hectal's live previews work this way).

  2. 2

    The browser keeps the two pages apart with the same-origin policy. An origin is the protocol + domain + port of a page (https://hectal.in is one origin; https://www.youtube.com is another). JavaScript in one origin can't read or change a page from another origin, so your scripts can't peek into a framed bank page, and the framed page can't read yours. The two can still talk safely by sending messages with postMessage.

  3. 3

    **title** is required for accessibility: screen readers announce it so users know what the frame contains before entering it ("Map of our office", not "iframe"). Also set width and height (or CSS size) so the page doesn't shift, and **loading="lazy"** for frames below the fold, since an iframe loads a whole page and is expensive.

  4. 4

    **sandbox** locks the frame down. An empty sandbox attribute applies every restriction: no scripts, no forms, no popups, no navigating your page, and the content is treated as a unique opaque origin (a nameless origin equal to nothing, so it can't use its own cookies or storage). You then add back only what you need with tokens such as allow-scripts, allow-forms, allow-popups and allow-same-origin. **Never combine allow-scripts and allow-same-origin for content from your own origin**: the framed script could reach up and delete the sandbox attribute.

  5. 5

    **allow sets the frame's Permissions Policy**: which powerful browser features it may use, such as fullscreen, autoplay, camera, microphone, geolocation or clipboard-write. Features not allowed are blocked inside the frame even if the framed site asks. **referrerpolicy** controls how much of your page's URL is sent to the framed site in the Referer header; the browser default is strict-origin-when-cross-origin (only your origin, like https://hectal.in/, is sent to other sites).

  6. 6

    The danger can also go the other way. Clickjacking is an attack where a malicious site loads your site in an invisible iframe and places it over a harmless-looking button, so a user who thinks they're clicking "Play" actually clicks "Delete account" on your site. The defence is on your server: send Content-Security-Policy: frame-ancestors 'self' (only your own pages may frame you; 'none' means nobody) and, for older browsers, X-Frame-Options: DENY or SAMEORIGIN. Then browsers refuse to show your page inside other sites.

Explain it without notes

01

What is the same-origin policy and how does it affect iframes?

02

What does an empty sandbox attribute block, and how do you add capabilities back?

03

What is clickjacking and how does a site defend against it?

04

What do allow and referrerpolicy control on an iframe?

Practice

01

Embed https://www.openstreetmap.org/export/embed.html?bbox=0,51.4,0.1,51.5 as an accessible, lazy, borderless 600×400 map.

02

Embed an untrusted widget from https://widget.example.com so that it can run scripts but cannot submit forms, open popups or navigate your page, and sends no referrer.

03

Write the response headers that stop every site, including yours, from framing your pages.

Trade-offs

  • ↔

    Embeds are quick to add and someone else maintains them, but they add weight, third-party cookies and tracking, and you can't control their look or uptime. A link or a static image is lighter when interaction isn't needed.

  • ↔

    A strict sandbox is safest but may break a widget; add tokens one at a time and test, instead of starting permissive.

Done when you can

  • I can embed a map or video with an iframe and a descriptive title.

  • I can lock a frame down with sandbox and add only the tokens it needs.

  • I can set allow and referrerpolicy and say what they do.

  • I can explain the same-origin policy and use postMessage safely.

  • I can explain clickjacking and set frame-ancestors / X-Frame-Options.