Recommended Free Tools
In a web page, add custom headers in the second argument to fetch(), under headers. For XMLHttpRequest, call setRequestHeader() after open() and before send(). Those examples run in a browser: browser security rules still control which headers can be sent, and a cross-origin custom header may require the API server to approve it through CORS. Node.js is a separate, server-side runtime; its global fetch and Headers APIs are documented for Node.js, but a Node process is not a browser page.
Send headers with browser fetch()
Pass a plain object or a Headers instance in the request options. The following example sends a GET request with an application-specific header and a bearer token, checks for an HTTP error status, then reads JSON:
async function loadItems() {
const response = await fetch("https://api.example.com/items", {
method: "GET",
headers: {
"X-Client-Version": "1.2.3",
"Authorization": "Bearer YOUR_TOKEN",
},
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
return response.json();
}
loadItems()
.then((data) => console.log(data))
.catch((error) => console.error("Request failed:", error));
Replace the example URL, version, and token with values appropriate to your API. The token shown is illustrative; avoid putting long-lived secrets in frontend code because code and requests originating in a browser are available to the user of that page.
Send a JSON body
For a JSON POST, set the content type and serialize the body. The custom request ID is an ordinary application header, but on a cross-origin request it can affect CORS preflight behavior.
Do these 3 things before closing this tab:
1Scan for outdated or missing drivers - takes under a minute2Repair Windows errors before they cause bigger problems3Fix the driver behind crashes, sound loss and screen glitches#1 Best Overall
const response = await fetch("https://api.example.com/items", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Request-Id": "abc123",
},
body: JSON.stringify({ name: "Example" }),
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const createdItem = await response.json();
Do not assume every unsuccessful operation throws an exception: fetch() can resolve with a response whose status is an HTTP error. Check response.ok or response.status before treating the response as a successful result.
Build headers incrementally
A Headers object is handy when values are conditional or assembled in separate parts of a program. Fetch accepts it in the same headers option. Header names are normalized, and surrounding whitespace in values is trimmed.
const headers = new Headers();
headers.set("X-Client-Version", "1.2.3");
headers.set("Authorization", "Bearer YOUR_TOKEN");
const response = await fetch("https://api.example.com/items", {
headers,
});
Use custom headers with XMLHttpRequest
With XHR, the order of operations is significant: open the request, set headers, then send it. This minimal GET example sets a custom header at the correct point:
const xhr = new XMLHttpRequest();
xhr.open("GET", "https://api.example.com/items");
xhr.setRequestHeader("X-Client-Version", "1.2.3");
xhr.send();
For response handling, XHR uses its event-based interface rather than Fetch’s Promise-based response flow:
const xhr = new XMLHttpRequest();
xhr.open("GET", "https://api.example.com/items");
xhr.setRequestHeader("X-Client-Version", "1.2.3");
xhr.responseType = "json";
xhr.addEventListener("load", () => {
if (xhr.status >= 200 && xhr.status < 300) {
console.log(xhr.response);
} else {
console.error(`HTTP ${xhr.status}`);
}
});
xhr.addEventListener("error", () => {
console.error("The request could not be completed.");
});
xhr.send();
Repeated calls to setRequestHeader() with the same header append values rather than replacing the earlier value. If the intent is to replace a value, build the header once rather than calling the method repeatedly. XHR remains available for existing code and interfaces that use it; Fetch is the modern Promise-based alternative described by MDN.
Which code runs in a browser and which runs in Node.js?
The examples above are for JavaScript executing in a page. That environment is governed by browser rules, including forbidden request headers and CORS. A Node.js program runs outside the page and uses Node’s runtime APIs; do not treat browser behavior as a general rule for every Node HTTP client.
Node.js global Fetch
Node.js documents global fetch and Headers. Its version history records global fetch as added in Node.js v18.0.0, and the global Headers class as no longer experimental in v21.0.0. If your program targets a different Node.js version or uses a different HTTP client, consult the documentation for that runtime and client before relying on a particular API.
For a Node.js process with global Fetch available, the request shape is familiar:
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
const response = await fetch("https://api.example.com/items", {
method: "GET",
headers: {
"X-Client-Version": "1.2.3",
"Authorization": "Bearer YOUR_TOKEN",
},
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const data = await response.json();
console.log(data);
This is server-side code, not code to paste into a browser page and not a way to grant a web page permission to call a third-party origin. Keep credentials on the server when the browser should not receive them.
Or skip the browser setup
If your actual task is to capture a webpage rather than send headers to your own API, ScreenshotNeo can return a screenshot or PDF from one GET request. For example, this cURL call saves a WebP capture:
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 API documentation for request options. It removes cookie banners, newsletter popups, and chat widgets before capture; bot checks, blank pages, and failed loads are not billed. Its MCP server lets AI agents take screenshots. The Free plan includes 1,000 shots a month with no card, and paid plans start at $5 for 3,000 shots. Sign up for ScreenshotNeo and get 1,000 free screenshots a month with no card.
Why a browser header may be missing or rejected
The browser controls certain request headers
Browser JavaScript does not have unrestricted control over raw HTTP headers. The forbidden request-header list includes fields such as Cookie, Host, Origin, Content-Length, and Connection, as well as names beginning with Sec-. Depending on the field and API, an attempted assignment may be ignored or prevented. Changing from a plain object to Headers, or trying XHR instead, does not make a browser-managed field available to page code.
Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Repair Windows errors before they cause bigger problemsFix Now →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →For cookies, use the browser’s cookie and credential mechanisms rather than trying to write a Cookie request header directly. For authentication, an Authorization header is allowed in ordinary cases, but XHR documentation notes it can be removed when a request is redirected cross-origin. Check the redirect path if a token appears to disappear.
Rank #4
Custom cross-origin headers can trigger an OPTIONS preflight
When a page requests a different origin, the browser applies CORS. A request that is not a CORS “simple request” can cause the browser to send an OPTIONS preflight first, describing the planned method and headers. The actual request is not sent if the preflight is rejected.
The server hosting the API must respond with CORS headers allowing the requesting origin, method, and requested header. In particular, its Access-Control-Allow-Headers response must permit the custom header. If you control that API, change its CORS configuration; changing only the frontend header code cannot grant permission to read a resource from another origin.
Credentialed CORS needs explicit server permission
For a cross-origin request that includes credentials, the server must explicitly allow the requesting origin and credentials. A wildcard origin is not valid for that case. Browser cookie policy still applies, so sending a credentials option does not guarantee that a cookie will be included.
Free tools Windows power users keep installed
One-click scans. No signup required.
Why no-cors is not a workaround
Setting Fetch’s mode to "no-cors" does not make a blocked API call readable. It restricts the methods and headers available to the request and returns an opaque response: JavaScript cannot inspect its body or headers. Use it only when that restricted outcome is suitable, not to bypass an API’s CORS policy.
Best Value
Debug header and CORS problems
- Confirm the execution environment. Determine whether the code is running in a browser page or a Node.js process. Browser restrictions apply to the former.
- Check the API call order. With Fetch, put headers in the options object passed to
fetch(). With XHR, callsetRequestHeader()afteropen()and beforesend(). - Check whether the field is browser-managed. If the name is forbidden, do not keep trying alternate syntax; use the appropriate browser mechanism or move the operation to a server you control.
- Inspect the network request and response. For a cross-origin failure, look for an
OPTIONSpreflight and the server’s CORS response. Confirm it permits the exact origin, method, and custom header used by the page. - Distinguish HTTP errors from network/CORS errors. If Fetch returns a response, inspect its status and
okproperty. If the browser blocks access because CORS checks fail, frontend code cannot read its response as though it were a normal success. - Check redirects and credentials. An XHR Authorization header may be removed on a cross-origin redirect; for credentialed cross-origin requests, verify explicit server permission and remember cookie policy remains in force.
Fetch versus XMLHttpRequest for headers
| Aspect | Fetch | XMLHttpRequest |
|---|---|---|
| Header configuration | Pass a plain object or Headers instance in the options object. |
Call setRequestHeader(). |
| Sequence | One request call with its options. | Call open(), set headers, then send(). |
| Response pattern | Promise-based. | Event/callback-oriented interface. |
| Browser restrictions | Forbidden headers and CORS still apply. | Forbidden headers and CORS still apply. |
Choose Fetch for new browser code when its Promise-based interface fits. Use XHR when maintaining code built around its interface. Neither API can override browser security policy.
FAQ
Do repeated XHR header calls overwrite the first value?
No. Repeated calls for the same header append values, so avoid duplicate calls when replacement is intended.
Does using a Headers object bypass forbidden-header rules?
No. It is another way to build a Fetch header list; it does not grant page code control over fields reserved to the browser.

