How to Embed a PDF in HTML Using an Iframe
Embed a PDF with a responsive iframe, accessible fallback link, correct headers, CSP, and fixes for blank or downloading viewers.
To embed a PDF in HTML, point an <iframe> at the PDF URL, set a meaningful title, give the frame a deliberate height, and place a normal link to the same PDF outside it. Browsers commonly use their built-in PDF viewer for this URL.
<iframe
src="/files/example.pdf"
title="Example PDF"
width="100%"
height="600"
loading="lazy">
</iframe>
<p>
<a href="/files/example.pdf">Open or download the PDF</a>
</p>
The src value must be a browser-reachable PDF URL. The separate link is necessary because an iframe has no child-content fallback when a PDF viewer is unavailable. See MDN’s iframe documentation and its embedded-content guidance.
1. Build a responsive PDF iframe
Hard-coding a pixel width can cause horizontal scrolling on phones. Keep the iframe at the container width and set its height with CSS.
.pdf-frame {
display: block;
width: 100%;
min-height: 32rem;
height: 75vh;
border: 0;
}
@media (max-width: fortyrem) {
.pdf-frame {
min-height: 24rem;
height: 70vh;
}
}
Replace fortyrem with a valid CSS value such as 40rem; it is written out here only to keep the example readable in prose.
<div class="pdf-container">
<iframe
class="pdf-frame"
src="/files/handbook.pdf"
title="Employee handbook PDF"
loading="lazy"
referrerpolicy="strict-origin-when-cross-origin">
</iframe>
<p>
<a href="/files/handbook.pdf">Open the employee handbook PDF</a>
</p>
</div>
Use a height that lets readers see a useful portion of a page. The parent page generally cannot discover the embedded PDF’s document height automatically because the PDF viewer is a replaced, embedded document.
2. Serve the PDF with the right headers
Your server controls whether the browser tries to display the PDF or downloads it. For inline display, return a PDF content type and an inline disposition.
Content-Type: application/pdf
Content-Disposition: inline; filename="handbook.pdf"
Content-Disposition: attachment tells the browser to treat the response as a download, so an iframe may download the file instead of showing the viewer. A redirect, authentication challenge, HTML error page, or expired signed URL can produce the same symptom as a broken PDF.
Example response configuration
Configure these headers wherever the PDF is generated or served. The exact syntax depends on your web server or framework, but the values should remain equivalent:
HTTP/1.1 200 OK
Content-Type: application/pdf
Content-Disposition: inline; filename="report.pdf"
Cache-Control: public, max-age=3600
Do not label an HTML error response as application/pdf. Verify the first response body is an actual PDF and that authentication works in a normal browser tab.
3. Keep the fallback link accessible
Some browsers, embedded webviews, assistive technology combinations, and locked-down environments may not provide a native PDF viewer. The direct link gives every user another path to open or download the document.
- Give the iframe a descriptive
title, such as “Quarterly financial report PDF”. - Use visible link text that identifies the document and action.
- Keep the link outside the iframe so it remains available if the viewer fails.
- For mobile readers, consider offering an HTML version or a download alongside the PDF.
4. Decide whether to use iframe, object, or embed
| Element | Useful when | Trade-off |
|---|---|---|
iframe |
You want simple embedding plus loading and referrerpolicy controls. |
It has no child-markup fallback for a failed PDF. |
object |
You need fallback HTML inside the element. | Policy and browser behavior differ from iframe. |
embed |
You are maintaining older embed-style markup. | It provides fewer practical controls and no normal fallback content. |
For a new page, an iframe plus a separate link is usually the clearest implementation. MDN identifies iframe as a strong choice when loading and referrer controls matter. All three rely on browser support for PDF viewing.
5. Configure loading, referrers, and security
Lazy loading
loading="lazy" lets the browser defer loading an off-screen PDF iframe. Use it for documents below the initial viewport. Omit it when the PDF is the primary content and should start loading immediately.
Referrer policy
referrerpolicy="strict-origin-when-cross-origin" limits the URL detail sent to another origin while preserving useful same-origin behavior. Choose a policy that matches your analytics and privacy requirements.
Sandbox
Do not add sandbox by default. MDN warns that sandboxing can prevent the browser’s built-in PDF viewer from loading. If a security policy requires it, test the exact browser and viewer combination and retain the direct link.
<!-- Start without sandbox. Add it only after testing your target browsers. -->
<iframe
src="/files/specification.pdf"
title="Product specification PDF"
loading="lazy">
</iframe>
Content Security Policy
If your site sends Content Security Policy, permit the PDF origin in frame-src. If you switch to object or embed, the relevant directive is object-src.
Content-Security-Policy: default-src 'self'; frame-src 'self' https://cdn.example.com;
Include the exact origin that serves the PDF, including its scheme and host. A CSP violation appears in the browser console and commonly leaves a blank frame.
6. Cross-origin PDFs and authentication
A PDF hosted on another origin can usually render in an iframe if that origin permits the request and returns a valid PDF. Same-origin rules still apply: parent-page JavaScript cannot generally inspect or manipulate a cross-origin PDF document.
For protected documents, use a short-lived URL or a server-side route that authenticates the user and streams the PDF. Do not put private tokens in a public iframe URL. Check whether your authentication flow permits iframe navigation, redirects, and cookies in the target browsers.
7. Troubleshooting a blank or downloading PDF
| Symptom | Likely cause | Fix |
|---|---|---|
| Blank iframe | Unsupported viewer, blocked CSP, invalid response, or sandbox. | Open the URL directly, inspect the network response, allow the origin in frame-src, and remove or review sandbox. |
| File downloads instead of rendering | Content-Disposition: attachment. |
Return Content-Disposition: inline when inline viewing is intended. |
| Browser shows an HTML error page | Authentication failure, redirect, missing file, or server exception. | Check status codes and response body; make sure the final response is a PDF. |
| PDF is cut off or unusable | Iframe height is too small or fixed for the layout. | Use responsive width and a deliberate height/min-height; test narrow screens. |
| Works directly but not in the page | CSP, framing policy, cookie, or referrer differences. | Inspect console and network errors, then compare headers and policies between direct and embedded requests. |
| Parent script cannot read the PDF | Cross-origin isolation. | Move the PDF to the same origin or communicate through a server endpoint; do not rely on DOM access across origins. |
8. Performance, reliability, and caching
- Defer below-the-fold documents: use lazy loading when the iframe is not immediately visible.
- Cache stable files: send a cache lifetime for PDFs that do not change often, and version the URL when content changes.
- Keep the fallback reliable: make the direct link point to the same tested URL as the iframe.
- Measure download size: large PDFs increase time to first page, especially on mobile networks. Compress images and fonts during PDF generation.
- Test real browsers: native PDF viewers differ in toolbar behavior, page scaling, printing, and accessibility support.
9. Or skip the browser setup
If your goal is a rendered image or PDF of a web page rather than an interactive embedded document, ScreenshotNeo provides a single screenshot API request. Its capture pipeline accepts cookie and consent banners, removes more than 60 known consent platforms plus newsletter popups and chat widgets before capture, and lets you turn each step off. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed; response headers identify the page verdict and billing result.
See the ScreenshotNeo API documentation for all options. A basic request looks like this:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com/report -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://example.com/report"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://example.com/report' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
ScreenshotNeo also supports PDF capture, full-page screenshots with lazy images loaded, element capture, custom CSS and JavaScript, waits, request blocking, headers and cookies, device presets, signed links, asynchronous jobs, bulk capture, and an MCP server with take_screenshot, get_page_info, and capture_pdf for AI agents. One thousand screenshots per month are free with no card; paid plans start at $5 for 3,000. Create a free ScreenshotNeo account.
10. FAQ
Can I embed a PDF from another domain?
Usually yes, if the URL is reachable and the server allows the request. Parent-page scripts still cannot inspect a cross-origin PDF.
Does an iframe need a width and height?
Give it both, either as attributes or responsive CSS. Without a usable height, the viewer may be clipped or appear empty.
Should I use a PDF.js viewer instead?
Use a custom viewer when you need consistent controls or programmatic document integration. For straightforward display, the native iframe viewer has less setup.
Why is the PDF link important if the iframe works?
It remains usable when a browser lacks a PDF viewer, a policy blocks embedding, or a reader prefers opening the document separately.


