Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.
Pass the headers as one JSON command-line argument, parse that string with JSON.parse(), assign the resulting object to page.customHeaders, and only then call page.open(). For headers needed on the first navigation only, pass the same object in page.open()‘s settings argument instead.
Use one JSON argument for the headers
PhantomJS exposes command-line values through system.args, and every value in that array is a string. A header collection is structured data, so serialize it as JSON in the shell and parse it inside the script. The argument order is:
- the script filename at
system.args[0]; - the target URL at
system.args[1]; - the JSON header object at
system.args[2].
This invocation sends an authorization header and a tracing header:
phantomjs headers.js https://example.com '{"Authorization":"Bearer TOKEN","X-Trace":"abc"}'
Use single quotes around the JSON in a POSIX shell so its double quotes reach PhantomJS unchanged. In a Windows shell, use the quoting rules of the shell that launches PhantomJS; the important result is still one argument containing valid JSON.
#1 Best Overall
Complete PhantomJS script
Save this as headers.js. It validates the argument count and JSON syntax before changing page settings, and it never prints the supplied credentials.
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: ' + e);
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;
page.open(url, function (status) {
console.log('Status: ' + status);
phantom.exit(status === 'success' ? 0 : 1);
});
Run it with:
phantomjs headers.js https://example.com '{"Authorization":"Bearer TOKEN","X-Trace":"abc"}'
page.customHeaders is page-wide: it supplies the additional headers for requests made by the page, not merely the first document request. Set it before the first page.open(); assigning it afterward cannot reliably affect a navigation that has already started.
Initial request only: use page.open settings
Sometimes a credential belongs only on the initial target request. In that case, do not make it a page-wide custom header. Parse the argument as before, then provide it in the settings object accepted by page.open:
The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →var system = require('system');
var webpage = require('webpage');
var page = webpage.create();
if (system.args.length < 3) {
console.log('Usage: phantomjs initial.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: ' + e);
phantom.exit(1);
}
var settings = {
operation: 'GET',
encoding: 'utf8',
headers: headers
};
page.open(url, settings, function (status) {
console.log('Status: ' + status);
phantom.exit(status === 'success' ? 0 : 1);
});
The settings form is appropriate when the header should apply to that navigation request only. Choose between the two mechanisms by scope:
| Mechanism | Scope | Best use | Data supplied |
|---|---|---|---|
page.customHeaders |
Page-wide additional request headers | A header that should accompany requests issued by the page | One parsed JavaScript object |
page.open(url, settings, callback) |
The request initiated by that page.open call |
A header needed only for the initial target request | Settings object with headers |
Pass different header shapes safely
Multiple headers
Represent each header as a property in the JSON object. Header names are strings, and values should normally be strings too:
phantomjs headers.js https://api.example.test '{"Authorization":"Bearer TOKEN","Accept":"application/json","X-Client":"phantom-job-7"}'
Empty or optional headers
An empty object, {}, is valid JSON and results in no additional custom headers. If your wrapper makes headers optional, pass {} explicitly rather than omitting the argument, or change the validation branch to supply an empty object.
Rank #2
Values containing spaces or punctuation
Keep the entire JSON object in one shell argument. Do not split at spaces, commas, or colons. If a token contains a quote, backslash, or newline, escape it according to JSON rules first and then apply the shell’s quoting rules.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Do not put secrets in diagnostics
Never echo the full command, dump system.args, or include the parsed object in error output. Process listings and shell history can also expose command-line arguments, so use a short-lived execution environment or a secret-injection wrapper when your operating system and deployment process provide one. The script above reports parsing failure without printing the submitted JSON.
Argument validation and exit behavior
Check both the number of arguments and the type of parsed value. JSON such as [], null, or a quoted string is syntactically valid but is not a header map. Reject it before assigning page.customHeaders. Returning a nonzero exit code on bad input lets a scheduler or CI job detect configuration errors.
In the callback, PhantomJS reports a status such as success or a failure status. Log only the status and exit with a matching code. A successful HTTP response is not the same as an authenticated application response: your target may still return a 401, 403, login page, or an application-level error. Inspect the page content or title when your job needs that distinction.
Shell portability checklist
- Confirm that the JSON reaches PhantomJS as one positional argument.
- Use valid JSON double quotes around property names and string values.
- Quote the URL separately when it contains shell metacharacters.
- Do not use a JavaScript object literal with unquoted property names;
JSON.parse()requires JSON. - Test the exact command under the same shell, user account, and scheduler that will run production jobs.
- Keep access tokens out of source control, logs, shell history, and shared process listings where possible.
Common failures and fixes
“Usage” appears immediately
Cause: fewer than two supplied values followed the script name, or the URL and headers were not passed as separate arguments.
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Scan for outdated or missing drivers - takes under a minute3Clear out junk files and repair common Windows errorsFix: provide both <url> and <headers-json>. Count arguments in the invoking wrapper, not by adding spaces inside the JSON.
“Invalid headers JSON”
Cause: shell quoting removed quotation marks, a trailing comma was used, or the value was JavaScript syntax rather than JSON.
Fix: run with a minimal object such as {"X-Test":"one"}, then add fields one at a time. Check the command in the actual shell used by the job.
Headers are not present on later requests
Cause: the script used page.open‘s request settings when page-wide behavior was required, or assigned page.customHeaders after navigation began.
Fix: assign page.customHeaders before the first page.open(). If only the first request should carry the value, keep the settings-object approach.
The server still returns 401 or 403
Cause: the token may be expired, the header name or value may be wrong, the endpoint may require cookies or another credential, or the server may apply authentication rules beyond the header.
Fix: verify the exact endpoint and token outside PhantomJS, check the response status and page content, and ensure the header is attached to the request scope you intended. Do not solve an authentication problem by printing the secret.
Rank #4
The script works interactively but not in a scheduler
Cause: a different shell changes quoting, environment variables are missing, or the scheduler exposes a different working directory and PhantomJS binary.
Fix: use absolute paths, explicitly select the shell, pass a known test header, and capture only non-sensitive status and error messages.
The page loads but scripts or resources behave differently
Cause: custom headers affect requests, while the target may also depend on cookies, user-agent behavior, redirects, or browser features. PhantomJS is a legacy command-line browser and its behavior can differ from modern engines.
Fix: reproduce the request with the same URL, headers, and redirect path; then verify the exact PhantomJS build. The documented command-line pattern targets PhantomJS 2.1.1, so test in the version actually deployed rather than assuming newer-browser behavior.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Performance, reliability, and security considerations
Keep the argument small and deterministic
A compact JSON object is easier to quote, audit, and retry than a series of individual name/value arguments. Build it in the calling program when headers are dynamic, serialize once, and pass the resulting string unchanged.
Windows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallCrashes, 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 minuteChoose the narrowest scope
Page-wide headers are convenient but can unintentionally accompany requests for redirects, scripts, images, or other resources. Use request settings for credentials that belong only to the first navigation. This minimizes accidental disclosure to another origin reached during the page lifecycle.
Best Value
Retry only the right failures
Retry transient launch, network, or timeout failures according to your job’s policy. Do not blindly retry 401 or 403 responses; those usually indicate invalid credentials or authorization and need correction. Record URL, non-secret status, and timing, but not the header object.
Treat PhantomJS as legacy
The command-line and system.args model is documented for PhantomJS 2.1.1. Pin and test the runtime used by your automation, and plan a migration if the target site requires modern browser features. This technique explains how to pass headers to an existing PhantomJS job; it does not make PhantomJS equivalent to a current browser.
Or skip the browser setup
If your actual goal is a clean website screenshot rather than browser scripting, ScreenshotNeo accepts headers directly through its API and avoids maintaining a PhantomJS process. Its consent step accepts cookie banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be disabled. Only clean shots are billed: bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and each response identifies the result with X-Page-Verdict and X-Billed headers.
Here is the one-call cURL form (replace the URL and key):
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 the header and capture options. The same endpoint can be called from 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)
Or 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. Every plan includes all features; 1,000 screenshots per month are free with no card, and paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.
When this pattern is the right choice
- Use
page.customHeaderswhen the same additional headers must accompany the page’s requests. - Use
page.opensettings when headers belong only to the initial GET. - Use JSON rather than separate name/value arguments so the script receives one structured value.
- Validate before navigation, quote for the real shell, and keep secrets out of logs.
- Pin and test the legacy PhantomJS runtime, especially when authentication, redirects, or modern site features matter.
Frequently Asked Questions
Can I pass each header as a separate PhantomJS argument?
You can design a parser for repeated name/value arguments, but the reliable pattern here is one JSON object. It preserves the relationship between names and values and avoids ambiguous argument boundaries.
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 →Should I use customHeaders for an Authorization token?
Only when that token is intended for the page-wide requests. If it belongs solely to the initial target request, put the parsed object in the settings argument to page.open instead.
What does system.args[0] contain?
It contains the script name. The first value supplied after that is system.args[1], followed by system.args[2] and later positional arguments.
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.

