ScreenshotNeo

BlogGuides

How PhantomJS User-Agent Changes Screenshot Rendering

Changing PhantomJS’s user-agent can change a screenshot when a site serves different content, but it does not change PhantomJS’s WebKit engine.

By the ScreenshotNeo team30 September 202610 min read

How PhantomJS User-Agent Changes Screenshot Rendering

Yes, changing the user-agent can change what appears in a PhantomJS screenshot—but only if the website responds differently to the new user-agent. The setting changes the identity PhantomJS sends with page resource requests; it does not replace PhantomJS’s WebKit rendering engine with Chrome, Safari, or a mobile browser. To compare results reliably, set page.settings.userAgent before page.open(), and keep the viewport, crop, page state, and capture timing fixed.

This distinction matters when a site serves different markup, stylesheets, or resources to different user-agent strings. A site may, for example, return a mobile-oriented page for a mobile user-agent. PhantomJS then renders that returned page with its own WebKit build. If the site serves the same content to both strings, the screenshot may not change at all.

1. What the user-agent setting actually changes

A user-agent is a request identity string. PhantomJS exposes it through the per-page settings.userAgent property. The PhantomJS API documentation describes it as the user-agent sent to the server when the web page requests resources. Set the value before opening the page: the settings are applied for the initial page.open(), and changing them afterward will not reliably change requests already made or subsequent behavior as expected. See the [PhantomJS page settings documentation](https://phantomjs.org/api/webpage/property/settings.html).

The user-agent can influence the response a site sends, while PhantomJS still renders that response with its own WebKit engine.
The user-agent can influence the response a site sends, while PhantomJS still renders that response with its own WebKit engine.

The request and rendering stages are separate:

  1. PhantomJS sends a request with the configured user-agent.
  2. The site may use that string to choose a response, such as different HTML, CSS, images, or scripts.
  3. PhantomJS’s WebKit engine renders the response it received.
  4. page.render() writes the selected output image.

A changed screenshot therefore does not prove that PhantomJS became the browser named by the string. It may only mean the server sent different page material. Conversely, if a site ignores the user-agent or serves the same page to both variants, changing the string may have no visible effect.

2. Complete PhantomJS example

This runnable script captures one URL using a browser-like user-agent. It sets the user-agent before navigation, specifies the viewport independently, waits for the page load callback, and writes a PNG. Save it as capture.js, then run it with a PhantomJS executable installed and available as phantomjs on your path.

var page = require('webpage').create();
var system = require('system');

var url = system.args[1] || 'https://example.com/';
page.settings.userAgent =
  'Mozilla/5.0 (Windows NT 10.0; Win64; x64) ' +
  'AppleWebKit/537.36 (KHTML, like Gecko) ' +
  'Chrome/120.0.0.0 Safari/537.36';
page.viewportSize = { width: 1365, height: 900 };

page.open(url, function (status) {
  if (status !== 'success') {
    console.log('Could not load ' + url + ' (status: ' + status + ')');
    phantom.exit(1);
    return;
  }

  page.render('capture.png');
  console.log('Saved capture.png');
  phantom.exit(0);
});

Run it with an optional URL:

phantomjs capture.js https://example.com/

The example user-agent is only a string sent in requests. It does not guarantee that the target site will accept it, identify it as a particular device, or return a specific layout. Use a user-agent appropriate to the behavior you need to examine, and verify the resulting page for the actual site.

3. Compare user-agents without mixing variables

For a useful before-and-after comparison, change only the user-agent. Keep the URL, viewport dimensions, clip rectangle, cookies, headers, page state, and capture timing the same. A page with rotating ads, personalized content, animations, or other dynamic elements can differ between runs for reasons unrelated to the user-agent.

A controlled comparison changes the user-agent while holding viewport, crop, page state, and capture timing steady.
A controlled comparison changes the user-agent while holding viewport, crop, page state, and capture timing steady.

Set a controlled viewport and crop

page.viewportSize controls the browser viewport. page.clipRect controls the rectangle written to the screenshot. These are independent from the user-agent. The [PhantomJS screen capture guide](https://phantomjs.org/screen-capture.html) shows the viewport, clip rectangle, and page.render() workflow.

page.viewportSize = { width: 1280, height: 800 };
page.clipRect = { top: 0, left: 0, width: 1280, height: 800 };

Use the same values for every comparison run. If you omit the clip rectangle, the rendered area may not correspond to a deliberately fixed crop. If testing a mobile response, choose a narrow viewport as well as a mobile user-agent; a user-agent alone does not shrink the viewport.

Capture two variants

One simple procedure is to run the script twice with the same dimensions and URL, changing only the assigned string, then save each output to a different filename. Inspect both the image and the loaded page. If output differs, look for changed page structure or assets rather than concluding that the rendering engine changed.

  1. Choose the exact target URL and record any redirect behavior.
  2. Run once with the default or baseline user-agent and save the image.
  3. Run again with the candidate user-agent and save a second image.
  4. Keep viewport and crop settings identical.
  5. Compare the rendered content and, where possible, inspect the response and resource requests.
  6. Repeat if the page contains dynamic content, using the same wait conditions and page state.

There is no universal result across websites. The outcome is specific to the site’s response logic and the PhantomJS build being used.

4. Important limits: user-agent is not browser emulation

Putting a Chrome or mobile browser string in page.settings.userAgent does not install or enable that browser’s layout, JavaScript, CSS, font, or API support. PhantomJS continues to render using WebKit. PhantomJS’s FAQ notes that the actual WebKit version depends on the libraries used to compile a particular build; the version string alone is not a reliable measure of supported web standards. See the [PhantomJS FAQ](https://phantomjs.org/faq.html).

This can produce a mixed result: the server may return a page intended for a modern browser, while PhantomJS’s older or differently compiled WebKit may not render all of that page as the named browser would. A user-agent can affect server-side content selection; it cannot make unsupported browser features appear in the rendering engine.

Variable What it affects What to hold steady
page.settings.userAgent Request identity; may influence the response selected by the site Change only this when isolating its effect
WebKit build How the returned HTML, CSS, and scripts are rendered Record the PhantomJS build for repeatability
page.viewportSize Available browser viewport dimensions Use the same width and height for each run
page.clipRect The region captured by page.render() Use the same crop or omit it consistently
Page state and timing Dynamic content, ads, and delayed resources visible at capture Use the same state and wait procedure

5. Options and settings to consider

The main setting for this question is page.settings.userAgent. It is per page, so assign it on the page instance that will open the target URL. Assigning it before navigation is the key configuration requirement. The full settings object includes other request-related controls, but they do not turn the user-agent into an engine switch.

  • User-agent: choose the request string whose server response you want to investigate. Do not assume a string guarantees a device-specific result.
  • Viewport size: use page.viewportSize to set the layout viewport. Set this separately when comparing desktop and mobile layouts.
  • Clip rectangle: use page.clipRect to constrain the screenshot region. It does not change the site’s response.
  • Capture timing: render after the page has reached a consistent state. The basic example waits for page.open() to report success, but client-rendered or delayed content may need an explicit site-appropriate wait.
  • Output: page.render() writes the screenshot to a file; select the filename and image format appropriate to the PhantomJS build and workflow.

Keep the comparison narrow. If you change the user-agent, viewport, cookies, headers, and timing at once, a changed image cannot tell you which change caused the difference.

6. Troubleshooting common problems

Symptom Likely cause What to do
The screenshot looks identical with both user-agents The site may serve the same response to both strings, or the visible layout may not depend on the selected response. Check the actual page content and requests. Test a site known to vary its response only as a diagnostic, and avoid treating one unchanged result as proof the setting was ignored.
The screenshot changes, but not as expected The site selected different content, or dynamic page state, ads, or timing also changed. Keep the viewport, crop, cookies, URL, and wait procedure fixed. Inspect the returned page and assets to identify what changed.
Changing the property after navigation has no effect The setting was applied too late; the initial page request already occurred. Assign page.settings.userAgent before page.open(), then reload for a fresh comparison.
A “Chrome” user-agent still renders like PhantomJS The string changes request identity, not the underlying engine. Use a browser whose rendering engine matches the target browser if engine fidelity is required. PhantomJS remains on its build’s WebKit.
The mobile user-agent produces a desktop-sized capture User-agent and viewport are independent; the viewport may still be wide. Set page.viewportSize to the desired dimensions and set page.clipRect if a fixed crop is needed.
The page is blank or incomplete Navigation failed, scripts or resources did not complete, the page requires interactions, or the capture happened too early. Check the page.open() status, inspect page errors and resource failures, and wait for a page-specific ready condition before rendering.
Repeated screenshots differ with identical settings The site may have dynamic content such as ads, animations, personalization, or time-dependent elements. Repeat with a stable page state and consistent timing. Treat dynamic differences as a confound, not evidence that the user-agent caused the change.

7. Performance, reliability, and cost

A user-agent string itself is a small configuration value. The work is dominated by loading the page and its resources, running scripts, waiting for the relevant content, and rendering the image. Capturing a page that loads many assets or runs substantial client-side code can take longer than capturing a simple static page. Do not infer performance from the user-agent label alone.

For repeatable work, record the URL, user-agent string, PhantomJS version/build, viewport, crop, and capture timing alongside the output. Use the same environment for comparisons, and make failures visible rather than silently treating an unsuccessful navigation as a valid screenshot. Since PhantomJS’s WebKit build depends on compilation libraries, two PhantomJS builds may not behave identically even with the same user-agent and page settings.

PhantomJS is legacy software, and its official documentation describes the behavior rather than promising current compatibility with every website. If the target depends on modern browser capabilities, server-side experiments with a custom user-agent may not be enough to produce a faithful image. Check the site-specific result before relying on a capture in production.

There is no published universal rate for how often changing the user-agent alters screenshot output. Cost depends on where and how you run PhantomJS—such as local compute or a hosted job system—rather than on the user-agent string itself. Budget for page loads, retries, and the actual capture infrastructure you choose.

8. Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server for developers. One GET request returns a PNG, JPEG, WebP, or PDF. The API supports custom user agents and viewport sizes, so you can request a particular server response without maintaining a PhantomJS capture script. See the ScreenshotNeo API documentation for parameters and response details.

curl -G "https://api.screenshotneo.com/v1/shot" \
  -d access_key=YOUR_API_KEY \
  --data-urlencode url=https://stripe.com \
  -o shot.webp
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)
const q = new URLSearchParams({
  access_key: 'YOUR_API_KEY',
  url: 'https://stripe.com'
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
const bytes = new Uint8Array(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', bytes));

With ScreenshotNeo, cookie banners are accepted and removed before the shot, along with more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits are never billed, and response headers report the page verdict and billing status. An MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots.

Sign up for ScreenshotNeo’s free plan to get 1,000 screenshots a month with no card.

9. FAQ

Does changing PhantomJS’s user-agent change the screenshot perspective?

It can change the result when the site serves different content based on the string. It does not change PhantomJS’s rendering engine. Set the user-agent before opening the page and test the target site directly.

Will a mobile user-agent make PhantomJS capture a mobile screen?

Not by itself. The site may return a mobile page, but viewport dimensions are configured separately with page.viewportSize. Set both when you want to investigate a mobile response at mobile dimensions.

Can I use a Chrome user-agent to get a Chrome screenshot?

No. The string can influence what the server sends, but PhantomJS still renders with the WebKit included in its build.

Why might two captures with the same user-agent differ?

Page content can vary because of timing, ads, personalization, animations, or other dynamic state. Keep those conditions as steady as possible and compare the loaded page as well as the image.

Is there one user-agent that works for every site?

No universal string guarantees a particular response. Site behavior varies, so use the string relevant to your test and verify what the target actually serves.

Sources