Skip to content
Danube Labs

Live website previews in a portfolio: what breaks and how to handle it

Embedding a client's live site in your portfolio is a great demo, until X-Frame-Options or a Content-Security-Policy quietly blanks the frame. A practical guide.

By Alexandru Secureanu2 min read

Our own portfolio shows client websites live, inside the page, so a visitor can scroll and click through the real thing. It is a better demo than a screenshot. It is also a small minefield, because a website decides for itself whether it may be embedded.

Two headers decide everything

A server can send X-Frame-Options: DENY (or SAMEORIGIN), or the newer Content-Security-Policy: frame-ancestors 'none'. Either one makes the browser refuse to render the page inside an <iframe> on another origin. You get a blank box or a browser error message, and there is no JavaScript event that tells you it happened.

Many hosting platforms and frameworks add these headers by default, for good reason: they stop clickjacking. Our founder's own portfolio sends frame-ancestors 'none', which is why it appears as a screenshot in our work section instead of a live frame.

Check on the server, not in the browser

Because the browser will not tell you, the check has to happen server-side. Our admin console has a "check embedding" button that sends a request to the URL and inspects the response headers. If either header forbids framing, the project is flagged and the site falls back to a screenshot with an "open live" link. The result is stored, so the public page never guesses.

Scale the frame, keep it usable

A desktop site squeezed into a 400-pixel card looks broken. We render the frame at a desktop width (1280 px) and scale it down with a CSS transform, so the card shows the site as its designer intended. Pointer events stay enabled, so visitors can scroll and click in the small preview. An expand button opens the same site full-screen with desktop, tablet and phone widths.

Do not load twenty sites at once

Each live frame is a full page load. We only create the frame when the card scrolls into view, and we show the screenshot until the frame has loaded. On slow connections the visitor still sees something immediately.

Sandbox with intent

The sandbox attribute restricts what the embedded page can do. We allow scripts, forms and same-origin access so the site behaves normally, and block top-level navigation so a link inside the frame cannot hijack the portfolio page.

Summary

  • Check X-Frame-Options and frame-ancestors on the server.
  • Store the result; fall back to a screenshot.
  • Render at desktop width and scale with a transform.
  • Load frames lazily, one per card, when visible.
  • Sandbox, but keep scripts and forms.
Share

Related articles

  • 8 min read

    Zero Data Retention in AI Routers: Does ZDR Survive Every Hop?

    Zero data retention means a provider stores neither your prompt nor the answer. Through a router the promise must hold at every hop. Here is what OpenRouter, OpenCode and Command Code document as of 4 October 2026.

    • Security
    • AI
    • LLM
  • 7 min read

    Does ChatGPT Use My Data for Training? Privacy Settings Guide

    On personal plans, ChatGPT may train on your chats unless you opt out. Compare ChatGPT, Gemini, Claude, Copilot and others: training defaults, opt-outs, temporary chats and retention, checked 4 October 2026.

    • AI
    • Security
    • Guides
  • 8 min read

    Cybersecurity for Small Business: The 10 Steps That Matter

    Cybersecurity for small business starts with ten habits: unique passwords, 2FA, payment checks, updates, tested 3-2-1 backups and an incident plan. Checklist and Romanian contacts included.

    • Security
    • Small business
    • Guides