October DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsClean PCRecommendedOne scan can reveal what keeps slowing WindowsLook for cleanup and repair opportunities.Run ScanOctober DealsAmazon USDeal season is back - check today's better picksAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
Base64

How to Replace an Intercepted Image With Base64 in Puppeteer

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Enable request interception, match the image request, decode the Base64 text into bytes, and fulfill the request with request.respond(). Set an accurate image MIME type, continue every request you do not replace, and guard against another handler resolving the same request.

Working implementation

This complete example replaces one HTTP(S) image while allowing the rest of the page to load normally. The Base64 value must contain the encoded image bytes, not a data: URL prefix.

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch();
const page = await browser.newPage();

const imageBase64 = '...'; // Base64-encoded PNG bytes
const targetImageUrl = 'https://example.test/assets/hero.png';
const imageBytes = Buffer.from(imageBase64, 'base64');

await page.setRequestInterception(true);

page.on('request', request => {
  if (request.isInterceptResolutionHandled()) return;

  if (request.url() === targetImageUrl) {
    request.respond({
      status: 200,
      contentType: 'image/png',
      body: imageBytes
    });
    return;
  }

  request.continue();
});

await page.goto('https://example.test', { waitUntil: 'networkidle2' });
// ...inspect the page or capture a screenshot...
await browser.close();

The sample uses a PNG payload, so it returns image/png. For JPEG bytes use image/jpeg; for WebP use image/webp. The response body can be a string or a byte array; Node’s Buffer is suitable for the byte-array form.

What each part does

Turn interception on before responding

page.setRequestInterception(true) activates Puppeteer’s request-resolution methods, including continue(), abort(), and respond(). Calling respond() without interception enabled does not provide the mocked network response.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Match the request you intend to replace

Exact URL matching is the safest choice when one known asset should change. Query strings, signed URLs, cache-busting parameters, redirects, and alternate hosts can make an exact string comparison too strict. In those cases, inspect the URL with new URL(request.url()) and compare its origin, pathname, or selected query parameters.

const requested = new URL(request.url());
const target = new URL('https://example.test/assets/hero.png');

if (requested.origin === target.origin &&
    requested.pathname === target.pathname &&
    request.resourceType() === 'image') {
  request.respond({
    status: 200,
    contentType: 'image/png',
    body: imageBytes
  });
  return;
}

request.resourceType() reports how the rendering engine classifies the request. It is useful as an additional check when a URL pattern might also match a stylesheet, script, or API call. URL matching alone is preferable when the asset’s address is uniquely stable.

Decode Base64 before sending it

Buffer.from(value, 'base64') converts the encoded text to the original binary bytes. Do not pass the Base64 characters as though they were already an image file. If your input is a data URL such as data:image/png;base64,iVBOR..., remove everything through the comma first:

const dataUrl = 'data:image/png;base64,iVBOR...';
const comma = dataUrl.indexOf(',');
if (comma === -1) throw new Error('Expected a data URL');
const imageBytes = Buffer.from(dataUrl.slice(comma + 1), 'base64');

Use the media type from the data URL, or otherwise determine it from the actual bytes, and send the matching contentType.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Resolve all other requests

Once interception is enabled, requests pause until they are continued, fulfilled, aborted, or completed from browser cache. The unconditional request.continue() branch is therefore required. Omitting it can leave scripts, stylesheets, fonts, and the document itself stalled, causing navigation timeouts or a partially rendered page.

Handling multiple or asynchronous request handlers

A single synchronous listener is easiest to reason about. Applications often have several listeners, middleware, or asynchronous work that decides which response to send. Puppeteer exposes request.isInterceptResolutionHandled() so a handler can avoid resolving a request twice.

page.on('request', async request => {
  if (request.isInterceptResolutionHandled()) return;

  const shouldReplace = request.url() === targetImageUrl;
  if (!shouldReplace) {
    request.continue();
    return;
  }

  // Any await creates time for another handler to resolve the request.
  const bytes = await loadBytesSomehow();

  // Check immediately before the resolution call.
  if (request.isInterceptResolutionHandled()) return;
  request.respond({
    status: 200,
    contentType: 'image/png',
    body: bytes
  });
});

The final check and the respond() (or continue()/abort()) call should remain together synchronously. Checking once at the top is not enough if an await occurs afterward.

If several listeners can act, establish one owner for each request or centralize interception in one listener. A double resolution can produce an interception error and leave the page in an unexpected state.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Choosing a matching strategy

Strategy Use it when Watch for
Exact URL The image address is stable and unique Query parameters, redirects, and cache-busting can change the string
Origin and pathname Only tracking or version parameters vary Different assets may share a pathname across environments
URL plus resourceType() A broad URL pattern could match non-image resources Classification is the browser’s perception, not a server-side MIME guarantee

Whichever strategy you choose, return the bytes and MIME type that belong together. A PNG body labeled as JPEG can fail to decode or render inconsistently.

Data URLs versus intercepted HTTP requests

request.respond() is for an intercepted network request. Puppeteer documents mocking responses for data: URL requests as unsupported; calling respond() for one is a no-op. If the page already uses a data URL, replace the element’s source in page content instead of trying to intercept that data URL:

await page.evaluate((dataUrl) => {
  const image = document.querySelector('#hero');
  if (!image) throw new Error('Image element not found');
  image.src = dataUrl;
}, 'data:image/png;base64,...');

This changes the DOM rather than mocking a network response. For an HTTP(S) image that the page requests normally, interception with respond() is the appropriate technique.

Navigation and capture timing

Install the interception listener before goto(), otherwise the target request may happen before your handler exists. Choose a navigation condition that matches the page: domcontentloaded is useful when you only need the initial document, while networkidle2 waits for a quieter network. A lazy-loaded image may not request its URL until it enters the viewport, so scroll or otherwise trigger the component before inspecting or capturing it.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
await page.goto('https://example.test', { waitUntil: 'domcontentloaded' });
await page.locator('#hero').scrollIntoViewIfNeeded();
await page.waitForSelector('#hero');
await page.screenshot({ path: 'result.png', fullPage: true });

Do not use an arbitrarily long delay as a substitute for a condition you can observe. Wait for the target selector, a known application state, or the request itself when that is what determines readiness.

Troubleshooting

The page hangs or navigation times out

  • Cause: an unmatched request was never resolved.
  • Fix: keep the pass-through branch and call request.continue() for every request you do not replace. Ensure an exception in your handler cannot skip both branches.

The replacement image is broken

  • Cause: the Base64 text includes an unremoved data-URL prefix, is malformed, or the declared MIME type does not match the bytes.
  • Fix: strip the prefix, decode with Buffer.from(..., 'base64'), and set contentType to the actual format.

The handler reports that the request was already handled

  • Cause: another listener, or another branch of asynchronous code, already called a resolution method.
  • Fix: check isInterceptResolutionHandled() at entry and again immediately after every await, then keep the final resolution call next to that second check.

The URL comparison never matches

  • Cause: the browser requested a redirected URL, a URL with a query parameter, or a different host.
  • Fix: log request.url(), compare parsed URL components, and add request.resourceType() === 'image' when appropriate.

Calling respond() has no effect

  • Cause: interception is disabled, or the target is a data: URL.
  • Fix: enable interception before navigation and target the original HTTP(S) request. Modify the DOM directly for data URLs.

The page shows the old image

  • Cause: the image completed from browser cache, so no network request needed interception.
  • Fix: use a cache-busting URL for your test asset or configure the page context so the request is made again. Do not assume an interceptor can replace a request that never reaches the network.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Operational considerations

Reliability

Keep replacement data in memory or load it before navigation when possible. If bytes must be fetched asynchronously, protect the post-await resolution with the handled-state check. Close the browser in a finally block in production code so failures do not leak Chromium processes.

Security

Only replace URLs you expect. Broad substring matches can accidentally mock scripts or third-party resources. Treat Base64 supplied by users as untrusted input, enforce a reasonable decoded size, and avoid logging the entire payload.

Performance

Base64 is a transport representation; decoding it once before the handler avoids repeated work for multiple navigations. A large inline value consumes memory in your Node process and in the browser response path. If the same bytes are reused, retain the decoded buffer rather than decoding for every request.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Or skip the browser setup

If your goal is a clean screenshot rather than testing a specific mocked response, ScreenshotNeo provides a single screenshot API call. It accepts cookie and consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be disabled. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the page verdict and billing status in X-Page-Verdict and X-Billed headers.

Use the API documentation at https://screenshotneo.com/docs/ for all options, including full-page and element captures, device presets, custom CSS and JavaScript, waits, blocked resources, cookies, headers, geolocation, PDFs, signed links, asynchronous jobs, bulk capture, and caching.

cURL

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

Python

import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)

Node.js

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

ScreenshotNeo also offers an MCP server with take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The Free plan includes 1,000 shots each month without a card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.

FAQ

Can I return a Base64 string directly as the response body?

Decode it first. The intercepted response represents image bytes, so pass the decoded Buffer (or another byte array) and the matching MIME type.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Does interception replace images loaded from CSS?

Yes, when the browser makes an HTTP(S) image request for the CSS resource. Match the requested URL and resolve it as you would an image element request.

Why is my replacement applied more than once?

The page may request the same URL multiple times, or multiple frames may load it. Track requests by URL, frame, or another identifier if your test requires a single replacement event.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Read next

Recommended PC Tool
Recommended PC Tool
Windows Errors? Fix Them Before They SpreadFree repair scan
Crashes, No Sound, or Screen Glitches?Free driver scan

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.