If Puppeteer downloads a video instead of showing a player, first determine what is being downloaded. It may be Chrome downloaded during Puppeteer installation, a direct media URL whose response says Content-Disposition: attachment, or a page that never embeds a <video> element. page.goto() only navigates to a URL and returns the main resource response; it is not a playback API. Inspect the final response after redirects, then inspect the page markup and media source.
1. Identify the download
There are two unrelated download paths that are often confused.
Puppeteer’s browser installation
The puppeteer package can download a compatible browser binary during installation. That happens before your script navigates anywhere. The installation guide distinguishes puppeteer-core, which does not automatically download Chrome. Changing page navigation code will not affect an installation download.
The target URL’s media response
If your script saves a file after page.goto(), or a navigation causes the browser to download a movie, inspect the target page and its network response. Page.goto() resolves with the main resource response; after redirects, that response represents the final destination.
The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →#1 Best Overall
2. Check the final HTTP response
Do not infer behavior from a .mp4, .webm, or other extension alone. Read the final response headers, including the response after every redirect.
Content-Disposition controls attachment behavior
MDN documents Content-Disposition: attachment as a request to download the response. inline, or no disposition header, is the browser-display path described by MDN, subject to the page context and supported media format.
Capture the headers with a command-line request:
curl -I -L "https://example.com/video"
Look at the last response in the redirect chain. A result such as:
Content-Disposition: attachment; filename="movie.mp4"
Content-Type: video/mp4
is a download response even though the MIME type identifies valid video. If you control the server, return an inline disposition (or omit the header) for a resource intended for playback. If you do not control it, Puppeteer cannot safely rewrite that server decision; use a player page that the site provides or obtain a response intended for inline display.
Recommended Free Tools
Verify the media type
The server must send a media type appropriate to the actual file. MDN’s video element documentation notes that web-server MIME configuration can affect handling of formats such as WebM. Correct the server’s MIME mapping and ensure the bytes match the declared type; changing only a filename is not a fix.
Rank #2
3. Decide whether you opened a player page or a raw file
A raw media URL is not the same thing as a webpage containing controls. A player page normally contains a <video> element and one or more sources. A direct file URL may simply return media bytes, which the browser can display in its built-in viewer only when the response is suitable for inline rendering.
Inspect the document
const videoCount = await page.$$eval('video', els => els.length);
console.log({ videoCount, url: page.url() });
const details = await page.$$eval('video', els => els.map(video => ({
src: video.currentSrc || video.src || null,
sources: [...video.querySelectorAll('source')].map(s => ({
src: s.src,
type: s.type || null
}))
})));
console.dir(details, { depth: null });
If videoCount is zero, you navigated to a page without an embedded player. Check for an iframe, a script-created player, or a link with a download action rather than trying to change download policy.
Check for an explicit download link
An anchor such as <a href="..." download> explicitly asks the browser to download its target. MDN notes that the attribute works for same-origin URLs and blob: or data: URLs, and that headers and user settings can still influence the result. Inspect the element and the click handler before changing server configuration.
const links = await page.$$eval('a[download]', els => els.map(a => ({
href: a.href,
download: a.getAttribute('download')
})));
console.log(links);
4. Use Puppeteer’s download settings for downloads only
Puppeteer exposes a browser-context downloadBehavior option. The ConnectOptions documentation and BrowserContextOptions documentation describe it as policy for file downloads. It can choose whether downloads are allowed and where they are written; it does not convert an attachment response into a player.
The Puppeteer files guide states: “Currently, Puppeteer does not offer a way to handle file downloads in a programmatic way.” Treat that as a boundary: configure download policy when your goal is to save a file, but fix the URL, headers, markup, or media configuration when your goal is playback.
Rank #3
Example diagnostic script
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch({ headless: true });
const page = await browser.newPage();
page.on('response', async response => {
if (response.request().isNavigationRequest()) {
const headers = response.headers();
console.log('final navigation', response.status(), response.url(), {
contentType: headers['content-type'],
contentDisposition: headers['content-disposition'] || null
});
}
});
await page.goto('https://example.com/player', { waitUntil: 'domcontentloaded' });
console.log('videos:', await page.$$eval('video', els => els.length));
await browser.close();
Replace the URL with the page you intend to view, not merely the media URL copied from a network panel. If the response is a redirect, the logged URL and headers describe the final navigation response.
5. Correct fixes by root cause
The script visits a raw attachment URL
Navigate to the site’s player page instead. Let that page create the <video> element and select its source. If you own the endpoint, change its response disposition to inline and serve a correct media type.
Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minutePC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11The page has no video element
There is nothing for Puppeteer to play. Use the site’s documented player route, wait for the component that creates the player, or handle the resource as a download. Do not expect page.goto() to manufacture controls.
The source is created after JavaScript runs
Wait for a selector or an application-specific readiness condition before inspecting it:
await page.goto('https://example.com/player', { waitUntil: 'networkidle2' });
await page.waitForSelector('video');
const source = await page.$eval('video', v => v.currentSrc || v.src);
console.log(source);
A delayed player is different from an attachment response. Waiting cannot override Content-Disposition: attachment.
Rank #4
The media type or bytes are wrong
Fix the origin server’s MIME mapping and encoding. Confirm that the declared type matches the actual container and codec supported by the browser. A WebM file served as an unrelated type can fail even when the URL looks correct.
6. Troubleshooting checklist
- A Chrome file appears during
npm install: this is Puppeteer’s browser installation. Considerpuppeteer-coreonly when you will provide and manage a browser executable yourself. page.goto()returns a response but no player appears: logresponse.url(), status,Content-Disposition, andContent-Type; then count<video>elements.- The response has
attachment: use a player URL or change the server response if you own it. Download policy cannot rewrite the header. - The page uses an
<a download>link: inspect same-origin,blob:, ordata:behavior and remove or change the link only if you control the page. - The player appears only after interaction: perform the required click and wait for
videoor the site’s ready state before readingcurrentSrc. - The video element exists but stays blank: verify the source URL, response MIME type, codec support, and whether the source itself redirects to an attachment.
- Redirects hide the cause: inspect every hop with
curl -I -Land log Puppeteer’s final response URL.
7. Or skip the browser setup
If your actual goal is a clean image or PDF of the player page rather than interactive playback, ScreenshotNeo provides a website screenshot API and MCP server. It accepts consent banners as a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be disabled. Only clean shots are billed, while bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, with the result identified by X-Page-Verdict and X-Billed headers. Its MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients.
One request is enough:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
See the ScreenshotNeo documentation for options such as full-page capture, element selectors, device presets, dark mode, custom JavaScript, waiting conditions, and PDF output. A free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.
8. Cost and reliability considerations
Do not repeatedly download a large media file just to test whether a page has a player. First inspect headers and markup, then perform a full navigation only when needed. Keep browser installation separate from application runs, log final URLs after redirects, and fail clearly when the response is an attachment or when no video element exists. These checks make the automation deterministic without pretending that Puppeteer can change server-side HTTP behavior.
Frequently Asked Questions
Can a different filename extension force inline playback?
No. The browser evaluates the response headers, media type, page context, and supported format; an extension alone does not turn an attachment into a player.
Free tools Windows power users keep installed
One-click scans. No signup required.
What should I record when reporting this bug?
Record the Puppeteer version, browser version, original and final URLs, redirect chain, final Content-Disposition and Content-Type headers, and whether the target document contains a video element.
The Bottom Line
Separate installation downloads from page downloads, inspect the final response headers, and confirm that you opened a real player page. Use download behavior only for files; it cannot override an attachment response or create video markup.
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.




