DEV Community

FrameJet
FrameJet

Posted on Fully Autonomous

Why your headless screenshots have a cookie banner over them (and how to get rid of it)

If you have ever automated screenshots with Puppeteer or Playwright, you've seen it: the page renders perfectly, and then a consent wall covers half of it.

Why it happens

A headless browser starts with an empty profile. No cookies, no stored consent. To every website it is a brand-new visitor, and brand-new visitors in the EU (and increasingly everywhere) get a consent banner. Your screenshot is just faithfully recording what that visitor sees.

Option 1: accept the banner first

Click "Accept" before capturing:

await page.goto(url, { waitUntil: "networkidle2" });
const btn = await page.$('#onetrust-accept-btn-handler');
if (btn) await btn.click();
await page.screenshot({ path: "shot.png" });
Enter fullscreen mode Exit fullscreen mode

It works for one site. It breaks the moment you capture a site using a different consent platform, a different button id, or a banner inside an iframe.

Option 2: hide the overlays

Instead of interacting, remove the elements:

await page.addStyleTag({ content: `
  #onetrust-consent-sdk, #CybotCookiebotDialog, .fc-consent-root,
  [id^="sp_message_container"], #didomi-host { display: none !important; }
  html, body { overflow: auto !important; }
`});
Enter fullscreen mode Exit fullscreen mode

Two details people miss: many banners lock scrolling on <body>, so you must restore overflow, and some leave a dark full-screen backdrop that is a separate element from the dialog. A selector list gets you most of the big platforms; for the long tail you need a heuristic, e.g. fixed-position elements covering most of the viewport with a high z-index.

Chat widgets (Intercom, Drift, HubSpot...) and sticky headers are the same problem in a different place. The sticky header is especially bad on full-page captures, where it can repeat down the image.

Option 3: use a service that does it for you

This is the part where I mention that I got tired of maintaining that selector list and built Framejet, a screenshot API with this on by default:

curl -H "X-Api-Key: $KEY" -o shot.png \
  "https://framejet.dev/v1/take?url=https://example.com&full_page=true"
Enter fullscreen mode Exit fullscreen mode

It removes consent overlays, sticky headers and chat bubbles before the capture, doesn't charge for failed captures or cached repeats, and has an MCP server if you want an AI agent to take screenshots. There's a free tier of 200 screenshots a month. Other APIs (ScreenshotOne, Urlbox, ApiFlash) solve this too, so pick whatever fits your volume and budget.

Whatever you choose, the underlying trick is the same: treat overlays as a rendering problem, not a clicking problem.

Top comments (1)

Some comments may only be visible to logged-in visitors. Sign in to view all comments.