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 →Pass headers as one JSON command-line argument, parse that string with system.args, assign the resulting object to page.customHeaders, and only then call page.open. For headers needed only on the first navigation, pass the same object through page.open‘s settings argument instead. PhantomJS command-line arguments are strings, so JSON serialization is the practical way to preserve a header map.
Working command and script
The following script accepts a target URL and a JSON header object. It validates both arguments before making a request and never prints the header values.
headers.js
var system = require('system');
var webpage = require('webpage');
var page = webpage.create();
if (system.args.length < 3) {
console.log('Usage: phantomjs headers.js <url> <headers-json>');
phantom.exit(1);
}
var url = system.args[1];
var headers;
try {
headers = JSON.parse(system.args[2]);
} catch (e) {
console.log('Invalid headers JSON');
phantom.exit(1);
}
page.customHeaders = headers;
page.open(url, function (status) {
console.log('Status: ' + status);
phantom.exit();
});
Run it from a shell
phantomjs headers.js 'https://example.com/private' '{"Authorization":"Bearer TOKEN","X-Trace":"abc"}'
system.args[0] is the script name. In this invocation, system.args[1] is the URL and system.args[2] is the serialized JSON object. The JSON is parsed into a JavaScript object before assignment. Set page.customHeaders before the first page.open; changing it after navigation has started is too late for that initial request.
Understanding PhantomJS argument positions
PhantomJS uses the form phantomjs [options] somescript.js [arg1 [arg2 [...]]]. The system.args array contains strings, with the script filename first and user-supplied values after it. Consequently, an object cannot be passed as a native JavaScript object from the shell. Serialize it as JSON and call JSON.parse inside the script.
#1 Best Overall
Fixed URL, one argument
If the URL is hard-coded, the JSON can be the first user argument:
var headers;
try {
headers = JSON.parse(system.args[1]);
} catch (e) {
console.log('Invalid headers JSON');
phantom.exit(1);
}
page.customHeaders = headers;
page.open('https://example.com/private', function (status) {
console.log(status);
phantom.exit();
});
Adjust the argument-count check to match that layout. Keeping the URL as an argument is generally more reusable and makes the usage contract explicit.
Choosing header scope
page.customHeaders: page-wide headers
Assigning page.customHeaders configures additional headers for requests made by the page. Use this when the same authorization, tracing, tenant, or feature header should accompany navigation and subsequent page activity. It is the mechanism shown in the main example.
page.open settings: initial request only
When a header belongs only on the first target request, supply it in the settings object accepted by page.open:
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Rank #2
var settings = {
operation: 'GET',
headers: headers
};
page.open(url, settings, function (status) {
console.log('Status: ' + status);
phantom.exit();
});
The settings object can also contain other request fields such as encoding and data. This per-request form prevents a header intended for the initial navigation from automatically becoming a page-wide default.
| Approach | Scope | Data shape | Best use |
|---|---|---|---|
page.customHeaders |
Additional headers for page-issued requests | Parsed JSON object | Shared authentication or tracing across the page |
page.open(url, settings, callback) |
Initial navigation request | Settings object containing headers |
One-time or navigation-only headers |
| Separate name/value arguments | Requires custom parsing and conventions | Multiple strings | Only when an integration cannot send JSON |
Shell quoting that works
JSON itself uses double quotation marks, so quote the entire JSON argument in a way that prevents the shell from removing those marks.
POSIX shells (Linux, macOS, CI)
phantomjs headers.js 'https://example.com' '{"Authorization":"Bearer TOKEN","X-Trace":"abc"}'
Outer single quotes preserve the JSON double quotes. If a value contains a single quote, construct the argument with a shell-safe escaping strategy or read it from an environment variable.
PowerShell
phantomjs headers.js https://example.com '{"Authorization":"Bearer TOKEN","X-Trace":"abc"}'
PowerShell’s quoting rules differ across versions and invocation contexts. If parsing fails, print only a sanitized representation such as the list of header names, not the argument containing credentials.
Free tools Windows power users keep installed
One-click scans. No signup required.
Environment-variable pattern
phantomjs headers.js "$TARGET_URL" "$HEADERS_JSON"
Environment variables reduce quoting errors in automation, but command-line arguments can still be visible to process-inspection tools on some systems. Treat bearer tokens, cookies, and API keys as secrets: restrict process access, avoid shell history where possible, and never log system.args or the raw JSON.
Validation and safer parsing
Validate the argument count and JSON syntax before assigning headers. A syntactically valid JSON value can still be the wrong type, so reject arrays, strings, and null when a header map is required:
if (system.args.length < 3) {
console.log('Usage: phantomjs headers.js <url> <headers-json>');
phantom.exit(1);
}
var headers;
try {
headers = JSON.parse(system.args[2]);
} catch (e) {
console.log('Invalid headers JSON');
phantom.exit(1);
}
if (!headers || Object.prototype.toString.call(headers) !== '[object Object]') {
console.log('Headers must be a JSON object');
phantom.exit(1);
}
page.customHeaders = headers;
Do not include control characters or malformed header names. Header values should be strings that the target server accepts. Keep credentials out of status messages, exception text, screenshots, and CI logs.
Common failures and fixes
“Invalid headers JSON”
- Cause: The shell stripped JSON quotation marks, or the argument contains a trailing comma.
- Fix: Quote the complete JSON object, test with a minimal value such as
{"X-Test":"1"}, and validate the exact string supplied by the calling process.
The script says the URL or headers argument is missing
- Cause: The script expects
system.args[1]andsystem.args[2], but only one user argument was supplied. - Fix: Use the documented invocation order or change the script’s indexes and usage check together.
The server still returns 401 or 403
- Cause: The token is expired, the header name/value is wrong, the endpoint requires cookies or additional context, or the server does not authorize PhantomJS’s request.
- Fix: Confirm the same URL and headers with an independent HTTP client, check token scope and expiry, and inspect the response status without logging secrets. A custom header cannot bypass an authentication policy that requires a cookie, CSRF token, or browser-generated value.
Header works for navigation but not later requests
- Cause: Headers were supplied in
page.open‘s settings, which targets the initial request. - Fix: Use
page.customHeadersbefore navigation when the header must accompany page-issued requests.
Header appears to be ignored
- Cause:
page.openwas called before assignment, the deployed PhantomJS build behaves differently, or the request is redirected to a host with different requirements. - Fix: Move assignment above the first
page.open, test the final redirected URL, and verify behavior in the exact PhantomJS build used in production.
Unexpected method or body
- Cause: The per-request settings object defaults do not match the endpoint’s contract.
- Fix: Set
operation,data, andencodingexplicitly when using thepage.openoverload.
Operational and security considerations
PhantomJS is a legacy runtime
The command-line and API pattern documented for PhantomJS 2.1.1 is a legacy-runtime approach. Pin the executable version in automation, test against the deployed build, and plan a migration if the target site requires modern TLS, JavaScript, or browser features that PhantomJS cannot provide.
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 minuteRank #4
Redirects, cookies, and browser state
A header added to the initial request does not guarantee that an upstream service will accept it after redirects or that it replaces cookies and CSRF state. Test the complete navigation path. Use the narrowest scope that satisfies the endpoint, especially for authorization headers.
Repeatability
For reliable jobs, make failures visible through exit codes, record status values without credentials, and keep URL, header schema, and PhantomJS version under deployment control. Avoid retrying non-idempotent requests merely because a page load timed out.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Or skip the browser setup
If your goal is a clean website screenshot rather than maintaining a PhantomJS script, ScreenshotNeo accepts a URL in one request and supports custom headers through its API options. Its browser workflow can accept cookie or consent banners, remove more than 60 known consent platforms plus newsletter popups and chat widgets, and lets you turn those steps off. Only clean shots are billed; bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and each response identifies the result with X-Page-Verdict and X-Billed headers.
See the ScreenshotNeo API documentation for all parameters, including custom headers, cookies, user agents, authorization, waits, blocking rules, viewport and device settings, full-page capture, PDF output, signed links, asynchronous jobs, and bulk capture.
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 provides 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 screenshots per month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account to try it.
Best Value
FAQ
Can I pass each header as a separate command-line argument?
Yes, but you must define and maintain your own name/value parsing convention. One JSON object is less ambiguous and preserves the header map in a single argument.
Should authorization headers be page-wide?
Only when every relevant page request requires them. Otherwise, use the page.open settings form so the credential is scoped to the initial navigation.
Why does the example use two indexes for user input?
PhantomJS reserves system.args[0] for the script name, making the URL and JSON values indexes 1 and 2.
Recommended Free Tools
Frequently Asked Questions
Can I pass each header as a separate command-line argument?
Yes, but you must define and maintain your own name/value parsing convention. One JSON object is less ambiguous and preserves the header map in a single argument.
Should authorization headers be page-wide?
Only when every relevant page request requires them. Otherwise, use the page.open settings form so the credential is scoped to the initial navigation.
Why does the example use two indexes for user input?
PhantomJS reserves system.args[0] for the script name, making the URL and JSON values indexes 1 and 2.
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.

