Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Clear out junk files and repair common Windows errorsFree Scan →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →To return a Puppeteer screenshot from an Express route, await page.screenshot(), convert the returned bytes to a Node.js Buffer, set the response type to image/png, and send the buffer with res.send(). This sends the image directly to the client; you do not need to save a temporary file. Close the browser in a finally block so cleanup runs after both successful and failed requests.
Return the screenshot as an image response
This example uses ES modules and the Express 4.x response API. Install Express and Puppeteer in your project, then place the route in your server entry point. The capture flow follows Puppeteer’s documented launch, navigation, screenshot, and close sequence. The example validates that the query parameter is a string, but production code should also validate and restrict which destinations it is allowed to visit.
import express from 'express';
import puppeteer from 'puppeteer';
const app = express();
app.get('/screenshot', async (req, res, next) => {
let browser;
try {
const url = req.query.url;
if (typeof url !== 'string') {
return res.status(400).json({ error: 'A URL is required' });
}
// In production, validate/allowlist destinations before navigating.
browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto(url, { waitUntil: 'networkidle2' });
const bytes = await page.screenshot({ type: 'png', fullPage: true });
res.type('png').send(Buffer.from(bytes));
} catch (error) {
if (res.headersSent) return next(error);
next(error);
} finally {
if (browser) await browser.close();
}
});
Start the server as usual for your app, then request /screenshot?url=https%3A%2F%2Fexample.com. The response body is PNG data, not JSON or a file path. Puppeteer’s screenshot API returns a Uint8Array by default; Buffer.from(bytes) makes the binary response explicit for Express.
Why set the content type?
Express can send a Buffer with res.send(), but a Buffer response defaults to application/octet-stream unless you set a type. res.type('png') tells clients the response is a PNG image. For JPEG, use res.type('jpeg') and request a JPEG screenshot.
#1 Best Overall
Why check whether headers were sent?
If an error happens after Express has started sending the response, the route must not try to send a second response. The res.headersSent check passes the error to Express’s error handling in that case; before headers are sent, next(error) delegates the failure normally.
Choose what the screenshot captures
Set capture options according to what the client needs. Puppeteer’s screenshot API documents these options; it does not promise a particular output size or capture time for any given page.
| Need | Option | Effect |
|---|---|---|
| Capture the visible viewport | Omit fullPage or set it to false |
Captures the current viewport. |
| Capture the whole page | fullPage: true |
Captures the full page rather than only the viewport. |
| Capture a bounded region | clip |
Specifies the region to capture. |
| Use a transparent background | omitBackground: true |
Omits the default page background where transparency is supported by the output. |
| Save an image on disk | path: 'capture.png' |
Writes the screenshot to a file. Omit path when the endpoint only needs to return bytes. |
| Use JPEG compression | type: 'jpeg', quality: 80 |
Requests JPEG and sets its quality from 0 to 100; quality does not apply to PNG. |
PNG is Puppeteer’s documented default screenshot type. Keep the response MIME type aligned with the chosen format: use image/png for PNG and image/jpeg for JPEG.
Handle destination URLs safely
A query parameter that controls browser navigation is a security boundary, not just input validation. The example’s string check rejects missing or repeated query values, but it does not make arbitrary destinations safe. In production, allow only the domains or URL patterns your application needs, and account for redirects and host resolution when enforcing that policy. The cited API documentation describes screenshot and response behavior; it does not define an SSRF defense policy for your application.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Rank #3
Manage browser work and reliability
- Wait for the intended page state. The example uses
waitUntil: 'networkidle2'before capture. Choose the navigation condition that matches the pages you support; a page with ongoing network activity may not reach an idle state. - Close resources on all paths. The
finallyblock closes the launched browser whether navigation, capture, or response handling succeeds or throws. - Account for in-flight screenshots. Puppeteer documents that creating pages and closing a page in the same BrowserContext wait for an ongoing screenshot to finish, whereas
bringToFront()does not. Avoid operations that assume an active screenshot has already completed. - Do not assume a pooling or concurrency design. The cited documentation does not establish production benchmarks, concurrency limits, deployment-specific launch flags, or a recommended browser-pooling architecture. Measure and design those for your own workload and deployment.
- Expect binary response sizing. Express documents automatic Content-Length handling for simple non-streaming responses such as this Buffer response. The screenshot API material does not quantify image file sizes.
Troubleshoot common failures
| Symptom | Likely cause | What to check |
|---|---|---|
| Request receives a 400 JSON error | The route did not receive one string-valued url query parameter. |
Send a URL as /screenshot?url=https%3A%2F%2Fexample.com; encode reserved URL characters. |
| Browser navigation or capture throws | The destination failed to load, navigation did not reach the configured state, or Puppeteer could not complete the capture. | Log the server-side error, verify the destination is reachable from the server, and reconsider the navigation condition for the pages you support. |
| Image displays as a download or generic binary | The response type was omitted or does not match the screenshot format. | Set res.type('png') for PNG or res.type('jpeg') for JPEG before sending. |
| Client receives an error after response output began | An error occurred after headers or part of the response had been sent. | Keep the res.headersSent guard and let Express’s error handling deal with the error instead of attempting another response. |
| Endpoint writes a file unexpectedly or cannot return the bytes | A screenshot path may have been supplied, or the returned bytes were not sent as a Buffer. | Omit path for an in-memory response and send Buffer.from(await page.screenshot(...)). |
| Browser resources accumulate | Cleanup may not run on every request path. | Keep browser closure in finally; verify that each successfully launched browser is closed. |
Version and compatibility notes
The cited Puppeteer documentation currently labels its shown release as 25.12.0, while the Express response documentation cited here is for Express 4.x. The Node.js HTTP documentation page reviewed is v26.10.0. These labels describe those documentation pages, not a requirement to use those exact versions. Check the APIs and runtime compatibility against the versions installed in your project.
Or skip the browser setup
ScreenshotNeo provides a screenshot API and MCP server for developers. For this route’s basic use case, call its endpoint and return the response bytes from your own service. The API accepts a URL and can return PNG, JPEG, WebP, or PDF; see the ScreenshotNeo API documentation for request options.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com -o shot.webp
ScreenshotNeo removes cookie banners, popups, and chat widgets before the shot. Bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Sign up for free.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Frequently Asked Questions
Can I return the screenshot without saving a file?
Yes. Leave out Puppeteer’s path option and send the returned bytes as a Buffer.
Do these 3 things before closing this tab:
1Fix the driver behind crashes, sound loss and screen glitches2Repair Windows errors before they cause bigger problems3Scan for outdated or missing drivers - takes under a minuteWhich response type should I use for a PNG screenshot?
Set the Express response type to image/png, for example with res.type('png').
Quick Recap
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.

