ScreenshotNeo

BlogHow-to

How to Hide Cookie Banners Before Taking Chrome Headless Website Screenshots

Remove a consent overlay from controlled test pages or handle it through the site’s actual controls. Capture the result reliably with Chrome Headless, Puppeteer, or Playwright.

By the ScreenshotNeo team4 October 20269 min read

To keep a cookie banner out of a Chrome Headless screenshot, first decide whether you are testing a site you control or capturing a third-party site. On your own test page, hide or remove the known consent component in the test environment immediately before capture. On a third-party site, use its actual consent controls for the choice you intend to make, then wait until the banner disappears. A CSS change can hide pixels without recording or communicating consent, so it is not a substitute for choosing through the site’s controls.

There is no universal cookie-banner selector or Chrome flag that safely removes every banner. Providers differ, and banners may load late or live in an iframe or shadow tree. Inspect each target and confirm the resulting screenshot.

1. Choose the right approach for the page

Situation Recommended handling What to verify
You own the page and need a repeatable visual test Use a test-only stylesheet, fixture, or test hook to suppress the known banner. The test configuration is isolated from normal users and the page content remains visible.
You are testing the consent experience Leave the banner visible and test its controls and state transitions. The selected choice is reflected in the page’s actual consent state.
You are capturing a third-party site Interact with its actual controls according to the intended choice, then wait for the interface to disappear. The intended control was used and the content beneath the banner is ready.

For a site you control, a narrow test-only CSS rule is often the simplest screenshot-specific solution once you have identified the component. Avoid broad rules such as hiding every fixed or high-z-index element: those can remove navigation, dialogs, or other useful content. A cosmetic change should not be presented as consent.

2. Capture once with Chrome’s command line

Chrome’s Headless mode runs without visible browser UI, and current Chrome uses unified Headless and headful modes. The command-line interface can save a screenshot, set the window size, and bound the wait before capture. See the Chrome Headless mode guide and command-line reference.

chrome --headless --no-sandbox --window-size=1440,1000 --timeout=10000 --screenshot=page.png https://example.com

Use the Chrome executable name or path appropriate to your installation. The example uses --no-sandbox only because some containerized environments require it; retain Chrome’s sandbox where your environment supports it. Replace the example URL with the page you are authorized to capture.

This direct command is useful for a basic capture, but it cannot express a site-specific consent interaction or confirm a particular selector has disappeared. If a deterministic banner treatment is required, use browser automation.

3. Use Puppeteer for a controlled test page

Puppeteer runs headless by default and provides navigation and viewport controls. Install it in a Node.js project with npm install puppeteer. The following example is for a page you control: replace the selector with the actual consent component’s selector, and apply the suppression only in the test capture.

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch({ headless: true });
try {
  const page = await browser.newPage();
  await page.setViewport({ width: 1440, height: 1000, deviceScaleFactor: 1 });
  await page.goto('https://example.com', { waitUntil: 'domcontentloaded', timeout: 30000 });

  // Replace with a selector inspected on the site you control.
  const consentSelector = '#cookie-consent';
  await page.waitForSelector(consentSelector, { timeout: 10000 }).catch(() => null);
  await page.addStyleTag({ content: `${consentSelector} { display: none !important; }` });

  // Wait for the content that should appear in the image.
  await page.waitForSelector('main', { visible: true, timeout: 15000 });
  await page.screenshot({ path: 'page.png', fullPage: true });
} finally {
  await browser.close();
}

If the banner is injected after the stylesheet, install the style before the relevant app code runs using Puppeteer’s page initialization facilities, or wait for the banner and reapply the rule. Prefer a test hook or a stable test attribute when you own the page. The example deliberately treats an absent banner as acceptable, but still waits for the main content before capture.

Interact with a third-party banner instead of concealing it

When your goal requires an actual consent choice, locate the real control and click it. The control’s accessible name and exact text depend on the target site and language; inspect the page rather than assuming a generic label.

const choice = page.getByRole('button', { name: 'Accept essential cookies', exact: true });
await choice.waitFor({ state: 'visible', timeout: 15000 });
await choice.click();
await page.locator('[role="dialog"]').waitFor({ state: 'hidden', timeout: 15000 });
await page.screenshot({ path: 'page.png', fullPage: true });

Replace the example accessible name and dialog selector with the target’s actual control and banner structure. Choose only the option that matches your intended interaction. Preserve the page’s resulting state as appropriate for your test; do not infer the state from the screenshot alone.

4. Use Playwright when you need explicit screenshot scope

Playwright supports viewport, element, and full-page screenshots. Its screenshot documentation describes the available capture scope: Playwright screenshots. Install with npm init playwright@latest or add the Playwright package using your project’s normal dependency workflow.

import { chromium } from 'playwright';

const browser = await chromium.launch({ headless: true });
try {
  const page = await browser.newPage({ viewport: { width: 1440, height: 1000 } });
  await page.goto('https://example.com', { waitUntil: 'domcontentloaded', timeout: 30000 });

  // For a site you control: use its inspected, test-only selector.
  const consent = page.locator('#cookie-consent');
  if (await consent.count()) {
    await consent.evaluateAll(nodes => nodes.forEach(node => node.remove()));
  }

  await page.locator('main').waitFor({ state: 'visible', timeout: 15000 });
  await page.screenshot({ path: 'page.png', fullPage: true });
} finally {
  await browser.close();
}

Removing a DOM node is a deliberate presentation change for a controlled test. If the banner is part of the behavior under test, do not remove it; test the interaction instead. Playwright also offers locator-based interaction, which is a better fit than DOM removal when you need to exercise the site’s real choice controls.

5. Make the screenshot repeatable

  1. Set the viewport. Choose fixed width and height and, when using an automation API, a deliberate device scale factor. This prevents layout changes caused by different window dimensions.
  2. Wait for a meaningful condition. Prefer a main-content selector or a known application-ready signal over a guessed delay. Use a timeout so a missing element fails clearly instead of hanging indefinitely.
  3. Handle the banner after identifying it. For controlled tests, use a narrow test-only rule or hook. For a real consent flow, interact with the actual control.
  4. Check that the overlay is gone and content remains. A screenshot can show the visual result, but inspect the image and relevant page state too.
  5. Select capture scope. Use viewport capture for the visible area, an element capture for one component, or full-page capture for the document. Full-page capture can be much taller and may trigger lazy-loaded content.
  6. Keep the environment stable. Use the same browser version, viewport, test data, and readiness condition across runs when comparing images.

6. Edge cases that change the implementation

  • Late injection: consent code may render after initial navigation. Wait for the specific component or application-ready signal, then handle it. A rule added too early may not match a later node unless it targets a stable selector.
  • Iframe: a banner inside an iframe is not necessarily reachable from the top-level document. Identify the frame and use the automation library’s frame APIs to inspect it. Cross-origin boundaries can limit what page scripts can inspect.
  • Shadow DOM: ordinary document selectors may not find content inside a shadow tree. Inspect the component and use supported locator or component-level test hooks.
  • Localization: button text varies by locale. Prefer a stable accessible name or site-specific test attribute, and set locale deliberately if your browser setup supports it.
  • Changing markup: provider updates can invalidate a selector. Treat a missing expected banner or control as a signal to review the test, not to broaden the selector until it hides unrelated UI.
  • Animation: a banner may be transitioning away when capture begins. Wait for it to become hidden or detached, not merely for a click promise to return.
  • Lazy content: full-page capture can expose images or sections that load only when scrolled. Wait for the relevant content or deliberately scroll through the page before capture if the test needs it.

7. Common errors and fixes

Symptom Likely cause Fix
Banner still appears in the image The selector was wrong, the banner rendered later, or it lives in a frame or shadow tree. Inspect the live page after navigation; target the actual component and wait for its state before capture.
Page content is also missing A broad CSS selector or removal rule matched more than the consent component. Narrow the rule to the identified banner and verify the main content before taking the screenshot.
Timeout waiting for the banner The banner is absent for this session, delayed, or structurally different. Decide whether absence is an expected case. Use a bounded optional wait only for the banner; keep a required wait for page content.
Click fails or has no visible effect The control label differs, it is obscured, or it is inside another browsing context. Inspect accessible names and frame structure; wait until the actual control is visible and actionable.
Screenshot is blank or incomplete Capture occurred before navigation or app rendering completed, or the page failed to load. Wait for the intended content selector and inspect navigation errors before capture.
Visual diffs vary between runs Viewport, browser state, fonts, asynchronous content, or animation timing changed. Pin the viewport and environment, wait on content conditions, and disable or finish animations in the controlled test setup.

8. Performance, reliability, and cost

Chrome CLI is the lightest workflow for a one-off capture. Puppeteer and Playwright add setup but make navigation, waits, interaction, and screenshot scope programmable. Avoid arbitrary long sleeps: they waste time on fast pages and can still be too short on slow ones. Wait for the page condition that matters, with a bounded timeout and a useful failure message.

For reliable visual checks, treat browser version, viewport, locale, network conditions, page state, and capture timing as inputs. Third-party pages can change without notice, so selectors and expected output need maintenance. A screenshot is evidence of rendered pixels; it does not by itself prove what consent state the site stored.

A 2019 study by Célestin Matte, Nataliia Bielova, and Cristiana Santos reported at least one suspected violation in 54% of 560 websites tested for IAB Europe Transparency and Consent Framework banner compliance. That is a historical result for the study’s sample, not a present-day estimate for all sites. It reinforces why visual concealment should not be confused with a valid consent interaction. Read the study.

Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server for developers. One request returns an image or PDF; see the API documentation for parameters and formats. Here is the one-call cURL example:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

ScreenshotNeo accepts cookie and consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be turned off. Bot checks, blank pages, and failed loads are never billed, and response headers identify the page verdict and billing status. Its MCP server lets AI agents use take_screenshot, get_page_info, and capture_pdf. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots.

Create a free ScreenshotNeo account for 1,000 screenshots a month with no card.

FAQ

No. Headless changes how Chrome runs, not the site’s consent interface. You need a site-specific test treatment or interaction.

Can I hide a banner without accepting cookies?

You can hide its visual element in a controlled test, but that does not establish or communicate a consent choice. For a real interaction, use the site’s controls.

Can I use the same selector on every website?

No. Banner markup and rendering vary. Inspect and maintain a selector or interaction for each target.

Which screenshot scope should I use?

Capture the viewport for what a visitor currently sees, a specific element for a component, or the full page when the entire document matters.