← Back to home
Security · Audit

Find hidden iframes
on any page.

Hidden iframes are real DOM elements — they just do not render anything you can see. Tracking pixels, OAuth helpers, A/B scaffolding, ad attribution beacons — they all live in the same place: iframes with zero size, display:none, or parked off-screen. Here is exactly how to surface every one of them.

Updated:11 May 2026Read:4 minLevel:Intermediate

What counts as “hidden”?

A hidden iframe is any frame that exists in the DOMbut is invisible to the user. It still loads its source, runs its scripts, fires its requests, and counts against the page's performance budget. It just does not occupy visible pixels.

There are six common patterns. They appear in production code from every analytics vendor, every ad network, most payment SDKs, and many legitimate libraries — so presence alone is not a verdict, but it is always worth a look.

  • <iframe src="..." width="0" height="0">Zero-sizeThe original tracker pattern. Still common in analytics and email open beacons.
  • <iframe style="display:none">Display noneRemoves the frame from layout entirely. Used by silent OAuth flows, some payment SDKs, A/B tooling.
  • <iframe style="visibility:hidden">Visibility hiddenFrame still occupies its box, but pixels are not painted. Less common but still seen in widget loaders.
  • <iframe width="1" height="1">1×1 tracking pixelSingle-pixel beacons for impression counting, conversion tracking, or fingerprinting.
  • <iframe style="position:absolute;left:-9999px">Off-screenReal rendered size, parked far outside the viewport. Used to avoid layout shift but still execute scripts.
  • <iframe src="about:blank">BlankEmpty frames used as sandboxes for HTML parsing, print preview, or just-in-case isolation.

Why it matters

Hidden iframes are how third parties piggy-back on your page. For a security audit, they are the highest-leverage place to look: a frame you cannot see can phone home to any domain, set storage in its own origin, and run code under permissions you granted to its parent script. For a privacy report, they are the actual instruments of tracking — pixel beacons, attribution iframes, fingerprinting probes. For performance, every hidden frame is still a network request.

You cannot decide what to keep until you can see what is there.

Hunt them in JavaScript

Open DevTools and run the following in the Console. It returns every iframe that is not actually visible to the user, with the reason it qualifies as hidden:

Paste in Console — returns one row per hidden frame.
Array.from(document.querySelectorAll('iframe'))
  .map(f => {
    const rect = f.getBoundingClientRect();
    const offParent = f.offsetParent === null;
    const displayNone = getComputedStyle(f).display === 'none';
    const tiny = rect.width <= 1 || rect.height <= 1;
    const offScreen =
      rect.right < 0 || rect.bottom < 0 ||
      rect.left > innerWidth || rect.top > innerHeight;
    const hidden = offParent || displayNone || tiny || offScreen;
    return hidden ? {
      src: f.src || '(blank)',
      size: `${Math.round(rect.width)}x${Math.round(rect.height)}`,
      reason: offParent ? 'display:none / detached' :
              displayNone ? 'display:none' :
              tiny ? '1×1 pixel' :
              offScreen ? 'off-screen' : 'unknown',
    } : null;
  })
  .filter(Boolean);

The snippet covers the six patterns above. getComputedStyle handles styles applied via stylesheets; offsetParent === null catches display:none on the frame or any ancestor; the bounding rect catches tiny and off-screen frames.

A small visibility-hidden trick

visibility: hidden does not set offsetParent to null — the frame still occupies its box. To catch it, check the computed visibility:

Array.from(document.querySelectorAll('iframe'))
  .filter(f => getComputedStyle(f).visibility === 'hidden');

One click with Iframe Detector

The Iframe Detector Chrome extension runs the same checks the script above does, then renders the result. Every hidden frame is tagged with a HIDDEN badge; the toolbar count tells you how many live on the page at a glance; the in-page highlight overlay draws a dashed outline around each one — so you can see what was invisible. Export the list to JSON, CSV, or Markdown and you have an audit report.

What to do once you find one

  1. Trust the origin. A hidden frame from js.stripe.comis Stripe's. One from connect.facebook.net is a Meta tracking helper. One from a domain you do not recognise is the one to investigate.
  2. Check what it loads.In DevTools' Network tab, filter by the frame's origin. Every request it fires is yours to inventory.
  3. Decide. Keep it (legitimate vendor), remove it (deprecated script, no contract), or sandbox it (sandbox="allow-scripts" strips most capabilities).
  4. Record it. Audits go stale fast. Export the full list with Iframe Detector and attach it to the ticket.

Frequently asked questions

No. Plenty of legitimate uses exist: payment SDKs (Stripe loads a hidden controller frame), passive consent banners, A/B-test scaffolding, OAuth silent-renewal flows. But every analytics network and most ad-attribution products also use 1×1 hidden iframes, so the same scan that surfaces a benign Stripe frame surfaces a tracker.

Both work. offsetWidth is faster and synchronous; getBoundingClientRect() returns sub-pixel precision and is the right choice if you need fractional sizes. For yes/no visibility checks the offset properties are simpler and the difference does not matter.

Open shadow roots are reachable: query the host element's shadowRoot and run querySelectorAll('iframe') on it. Closed shadow roots are not — by design, the page that creates them does not want them traversed. Iframe Detector enumerates open shadow roots automatically.

Not necessarily — they are just empty. Many libraries create blank iframes as sandboxes for safe HTML parsing or print preview, and they may be 0×0 or full-size depending on intent. The visibility check still applies: if it has zero rendered area, list it.

Yes — display:none, visibility:hidden, zero width or height, and 1×1 frames are all explicitly marked with a HIDDEN badge in the popup list. Off-screen frames moved via CSS are caught too.