To embed a website in HTML, add an iframe with the page's URL in src and a descriptive title: <iframe src="https://example.com" title="Example" width="800" height="600"></iframe>. It only works if the site allows framing. Many sites block it with X-Frame-Options or CSP frame-ancestors, and your page can't override that.
How to embed a website with the iframe tag
An <iframe> (inline frame) creates a separate browsing context inside your page. The framed page loads from its own server, in the visitor's browser, with its own scripts and cookies. You're not copying the content, you're opening a window onto it, which is why the other site gets a say in whether it can be framed.
This is the minimal iframe code that works:
<iframe src="https://example.com" title="Example Domain"></iframe>
Without a size, the browser renders it at 300×150 pixels, which is rarely what you want. A more practical starting point looks like this:
<iframe
src="https://example.com/pricing"
title="Example pricing table"
width="800"
height="600"
loading="lazy"
referrerpolicy="strict-origin-when-cross-origin"
style="border:0"></iframe>
A few points that trip people up:
- Always add
title. Screen readers announce it when a user reaches the frame. Describe the content ("Store opening hours map"), not the element ("iframe"). - Don't put fallback text between the tags. Modern browsers ignore anything inside
<iframe></iframe>. If you want a fallback, put a normal link next to the frame. - Use
https://URLs. Anhttp://frame inside anhttps://page is blocked as mixed content. Local addresses such ashttp://localhostand127.0.0.1are the exception.
Iframe attributes: a complete reference
These are the iframe attributes you'll actually use. Older attributes such as frameborder, scrolling and marginwidth are obsolete; handle those with CSS instead.
| Attribute | What it does | Example and notes |
|---|---|---|
src | URL of the page to load. | src="https://example.com/widget". Must be HTTPS on an HTTPS page. |
srcdoc | Inline HTML to render instead of a URL. | Takes priority over src if both are set. See the srcdoc section. |
title | Accessible name for the frame. | title="Checkout form". Treat it as required. |
width / height | Size in CSS pixels, written without units. | width="560" height="315". Default is 300×150. CSS overrides these. |
loading | When to load the frame. | lazy defers off-screen frames until the user scrolls near them; eager is the default. |
allow | Permissions Policy: which powerful features the frame may use. | allow="fullscreen; autoplay; clipboard-write; encrypted-media; picture-in-picture". Also camera, microphone, geolocation, payment, web-share, storage-access and more. |
allowfullscreen | Legacy boolean that lets the frame go full screen. | Same effect as allow="fullscreen". Still common in embed snippets. |
sandbox | Applies extra restrictions to the framed content. | Empty sandbox applies all restrictions; tokens such as allow-scripts or allow-forms opt back in. |
referrerpolicy | How much of your page's URL is sent as the Referer header. | no-referrer, origin, strict-origin-when-cross-origin (the browser default) and others. |
name | Names the frame so links and forms can target it. | name="preview", then <a href="…" target="preview">. |
Delegating camera or geolocation with allow only makes the feature available to the frame. The visitor still sees the browser's permission prompt, and autoplay with sound is still subject to the browser's autoplay rules.
The sandbox tokens deserve their own page; the complete guide to the iframe sandbox attribute covers every one.
How to embed YouTube, Google Maps, Google Docs and Vimeo
Big platforms block their normal pages from being framed but publish embed URLs built for it. Pasting the address-bar URL into src is the most common reason an embed fails.
YouTube
Use https://www.youtube.com/embed/VIDEO_ID, not /watch?v=VIDEO_ID. The watch page sends X-Frame-Options: SAMEORIGIN, so it will refuse to load on your site. The easiest route is Share → Embed under the video, which gives you the full snippet.
<iframe
src="https://www.youtube-nocookie.com/embed/VIDEO_ID?start=30"
title="YouTube video: Product walkthrough"
width="560" height="315"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
referrerpolicy="strict-origin-when-cross-origin"
allowfullscreen
loading="lazy"
style="border:0"></iframe>
Swapping youtube.com for youtube-nocookie.com switches on YouTube's privacy-enhanced mode, which reduces tracking before the visitor presses play. It doesn't make the embed tracking-free once the video runs. Useful query parameters include start (seconds), autoplay=1 (usually needs mute=1 to actually autoplay), and rel=0, which limits end-screen suggestions to the same channel.
YouTube's own snippet sets referrerpolicy="strict-origin-when-cross-origin". Setting no-referrer can make the player show an error instead of the video, so leave it as YouTube provides it.
Google Maps
A regular google.com/maps/place/… link won't frame. To get embeddable code:
- Open Google Maps and search for the place or route.
- Click Share, then the Embed a map tab.
- Choose a size and click Copy HTML.
The result points at https://www.google.com/maps/embed?pb=…; don't edit the encoded pb value by hand. To build map URLs in code (one per store, say), use the Maps Embed API, which takes readable parameters and a Google Cloud API key:
<iframe
src="https://www.google.com/maps/embed/v1/place?key=YOUR_API_KEY&q=Eiffel+Tower,Paris+France"
title="Map: Eiffel Tower"
width="600" height="450"
loading="lazy"
referrerpolicy="no-referrer-when-downgrade"
style="border:0"
allowfullscreen></iframe>
Google Docs, Sheets, Slides and Forms
For Docs, Sheets and Slides, go to File → Share → Publish to web, pick the Embed tab, click Publish and copy the iframe. The published version is public to anyone who has the link, and by default Google republishes it automatically when you edit the file. Use the published URL rather than your /edit link; the editor depends on the visitor being signed in with access, so it isn't a reliable embed.
For Forms, click Send, choose the <> embed icon and copy the code. The URL ends in viewform?embedded=true:
<iframe
src="https://docs.google.com/forms/d/e/FORM_ID/viewform?embedded=true"
title="Event sign-up form"
width="640" height="900"
loading="lazy"
style="border:0"></iframe>
Vimeo
Vimeo's player lives at https://player.vimeo.com/video/VIDEO_ID. Get the code from Share → Embed. Unlisted videos include an h= hash parameter that must stay in the URL. If a Vimeo embed shows a privacy message instead of the video, the owner has restricted embedding to specific domains in the video's privacy settings.
<iframe
src="https://player.vimeo.com/video/VIDEO_ID"
title="Vimeo video: Customer story"
width="640" height="360"
allow="autoplay; fullscreen; picture-in-picture"
loading="lazy"
style="border:0"></iframe>
Other platforms follow the same pattern: look for an Embed or Share option and paste the code exactly as given.
What is srcdoc? Embedding inline HTML
The srcdoc attribute renders an HTML string directly, with no network request. It's how code playgrounds, email previews and template editors show live output. If an iframe has both srcdoc and src, srcdoc wins.
Because the HTML lives inside an attribute, you must escape & as & and double quotes as ":
<iframe
title="Rendered preview"
sandbox
srcdoc="<h1>Hello</h1><p>Tom & Jerry said "hi".</p>"></iframe>
From JavaScript it's simpler: set frame.srcdoc = html and the browser handles the escaping.
A srcdoc document inherits the parent page's origin, so its scripts can read your page, cookies and storage. For any HTML you didn't write yourself (user content, emails, AI output), add sandbox and don't include allow-same-origin. If it needs JavaScript, sandbox="allow-scripts" alone keeps it in an opaque origin.
Relative URLs inside srcdoc resolve against the parent page's URL, so <img src="/logo.svg"> loads from your site.
How to style an iframe: border, size and aspect ratio
An iframe is an inline element with a default border. This CSS fixes both and makes it scale with its container:
iframe {
display: block; /* removes the gap below inline elements */
border: 0; /* replaces the obsolete frameborder="0" */
width: 100%;
height: auto;
aspect-ratio: 16 / 9; /* keeps video proportions at any width */
}
iframe.map { aspect-ratio: 4 / 3; }
iframe.form { aspect-ratio: auto; height: 900px; }
CSS aspect-ratio has been supported in all modern browsers since 2021, so you no longer need the old padding-bottom wrapper hack. It works well for videos and maps, where the proportions are fixed.
Pages and forms are different: their height depends on content. An iframe never grows to fit its content by itself, and your page can't measure a cross-origin frame. The framed page has to report its height with postMessage. The guide to responsive and auto-resizing iframes has working code for both sides.
Add loading="lazy" to every iframe below the fold. For YouTube, consider a facade: show a thumbnail and swap in the iframe on click. Video players are heavy, and deferring them helps Core Web Vitals. See how iframes affect SEO and page speed for the trade-offs.
Why some websites won't load in an iframe
If you embed a site and get a blank box or a grey "refused to connect" page, the site is almost always blocking you on purpose. The main causes:
| Cause | What you see | Who can fix it |
|---|---|---|
X-Frame-Options: DENY or SAMEORIGIN | Chrome: "example.com refused to connect", plus a console error naming the header. | The site owner. |
CSP frame-ancestors that doesn't list your origin | Same as above; the console names frame-ancestors. | The site owner. |
Mixed content (http:// frame on an https:// page) | Blank frame and a mixed-content console error. | You: switch to HTTPS. |
Your own CSP frame-src or child-src | Blank frame and a CSP violation on your page. | You: add the origin to frame-src. |
| Frame-busting JavaScript or a bot challenge | Your page gets redirected, or the frame shows a challenge. | Usually the site owner. |
| Blocked third-party cookies | The page loads but shows as logged out. | The site owner, via cookie settings or the Storage Access API. |
X-Frame-Options and frame-ancestors are response headers on the other site. Nothing you put in your HTML can override them. The two options are to use the site's official embed URL, if it has one, or to link to the page instead. Proxying the page through your own server to strip the headers tends to break logins and scripts, and may break the site's terms of use.
For a step-by-step diagnosis, read why an iframe says "refused to connect" and how to fix it. If you run the site you're trying to embed, X-Frame-Options vs CSP frame-ancestors shows how to allow specific domains safely.
Iframe security basics
The same-origin policy stops a cross-origin frame from reading your page, and you from reading it. But a framed page can still open popups, show dialogs, use permissions you delegated and try to navigate your page away. Keep embeds tight:
- Embed sources you trust. You're running someone else's code in your visitors' browsers.
- Sandbox untrusted content. Start with an empty
sandboxand add only the tokens the embed needs. Never combineallow-scriptsandallow-same-originfor same-origin content, because the frame can then remove its own sandbox. - Delegate the minimum in
allow. A map needs no camera. A video player needsfullscreenand perhapsautoplay. - Limit the referrer with
referrerpolicyif your URLs contain anything private, such as tokens or search terms. - Validate messages. If the frame talks to your page, check
event.originagainst an allowlist on every message. The postMessage guide shows the pattern.
<!-- A third-party widget that needs scripts and forms, nothing else -->
<iframe
src="https://widgets.example.net/feedback"
title="Feedback widget"
sandbox="allow-scripts allow-forms allow-same-origin"
referrerpolicy="origin"
loading="lazy"
style="border:0; width:100%; height:420px"></iframe>
allow-same-origin is fine here because the widget is on another origin: it keeps its own cookies but still can't reach into your page.
To stop other sites framing yours, send frame-ancestors 'self' plus X-Frame-Options: SAMEORIGIN; the homepage prevention section has server snippets.
How to check if a URL can be embedded
The quickest check is to paste the URL into the testiframe.com iframe tester. It loads the page in a live frame and runs a server-side check that reports:
- the X-Frame-Options and CSP
frame-ancestorsvalues, and whether they allow framing; - mixed-content problems, likely frame-busting scripts and bot challenges;
- how cookies will behave in Chrome and Edge, Firefox and Safari;
- whether the page would load when framed from your origin, using the "Would it load on your site?" check.
You can also link straight to a result. Comparing a YouTube watch page with its embed URL makes the difference obvious: test a watch URL, then test the embed URL. The tester also has device-size presets, sandbox and allow toggles, and an embed-code generator that writes the snippet for you. Local addresses like http://localhost:3000 work for the preview, though the server check can't reach them.
From a terminal, you can read the headers yourself:
curl -sI https://example.com | grep -iE 'x-frame-options|content-security-policy'
No output means neither header is set, though a login redirect or your own CSP can still get in the way.
FAQ
How do I embed a website in HTML?
Add an iframe element with the page's URL in the src attribute and a short title, for example <iframe src="https://example.com" title="Example site" width="800" height="600"></iframe>. Size it with CSS and add loading="lazy" if it sits below the fold. It only works if the other site allows framing.
Why does my iframe say "refused to connect"?
The site you are embedding sends an X-Frame-Options header or a CSP frame-ancestors directive that doesn't allow your page to frame it. Chrome shows "refused to connect" and logs the reason in the console. Only the site owner can change those headers.
Can I embed any website in an iframe?
No. Many sites, including most banks, social networks and large web apps, block framing to protect against clickjacking. Use the site's official embed option if it has one, or link to the page instead.
How do I embed a YouTube video in HTML?
Use the /embed/ URL, not the /watch?v= page. Click Share, then Embed, under the video and copy the iframe code, or build it yourself with https://www.youtube.com/embed/VIDEO_ID. Swap the domain for youtube-nocookie.com to use privacy-enhanced mode.
How do I make an iframe responsive?
Give it width: 100%, height: auto and a CSS aspect-ratio such as 16 / 9, and remove the border with border: 0. For content whose height changes, the framed page has to report its height to the parent with postMessage.
What is the difference between src and srcdoc?
src loads a document from a URL. srcdoc takes an HTML string and renders it directly. If both are set, srcdoc wins. A srcdoc document shares your page's origin, so sandbox it when the HTML isn't fully trusted.