Javascript Response Headers


Main Point

You cannot retrieve a value from a javascript response header using normal object property notation. Instead you must use the .get('header_name') syntax.

Story

I was trying to use the GitHub api to paginate through some results. The way their api works is they will provide a “link” header in the response to let you programmatically know what URL to use in the next request in order to get the next set of values. My script kept failing.

const response = await fetch(URL, { headers })

// --------------------------------------------------------
console.log(response.headers)
// ------
// OUTPUT
// ------
// Headers {
//   date: 'Wed, 01 Oct 2025 19:40:26 GMT',
//   'content-type': 'application/json; charset=utf-8',
//   'cache-control': 'private, max-age=60, s-maxage=60',
//   vary: 'Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With',
//   etag: 'W/"8bbc9d4cc91cd490eddc9a04766a694bc0524e10b6b1ace25c97fa08547f47a4"',
//   'x-oauth-scopes': 'admin:enterprise, admin:gpg_key, admin:org, admin:org_hook, admin:public_key, admin:repo_hook, admin:ssh_signing_key, audit_log, codespace, copilot, delete:packages, delete_repo, gist, notifications, project, repo, user, workflow, write:discussion, write:network_configurations, write:packages',
//   'x-accepted-oauth-scopes': '',
//   'github-authentication-token-expiration': '2025-12-08 20:51:03 UTC',
//   'x-github-media-type': 'github.v3; format=json',
//   link: '<https://api.github.com/organizations/105071744/repos?page=2>; rel="next", <https://api.github.com/organizations/105071744/repos?page=16>; rel="last"',
//   'x-github-api-version-selected': '2022-11-28',
//   'x-ratelimit-limit': '5000',
//   'x-ratelimit-remaining': '4992',
//   'x-ratelimit-reset': '1759350720',
//   'x-ratelimit-used': '8',
//   'x-ratelimit-resource': 'core',
//   'access-control-expose-headers': 'ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset',
//   'access-control-allow-origin': '*',
//   'strict-transport-security': 'max-age=31536000; includeSubdomains; preload',
//   'x-frame-options': 'deny',
//   'x-content-type-options': 'nosniff',
//   'x-xss-protection': '0',
//   'referrer-policy': 'origin-when-cross-origin, strict-origin-when-cross-origin',
//   'content-security-policy': "default-src 'none'",
//   'content-encoding': 'gzip',
//   'transfer-encoding': 'chunked',
//   'x-github-request-id': 'B2C6:2D8744:F123E:35B2D8:68DD83A9',
//   server: 'github.com'
// }

// --------------------------------------------------------
console.log(response.headers.link)
// ------
// OUTPUT
// ------
// undefined

In order to get the value of the link header (or any header in the response object for that matter) is to use the ‘.get(key)’ syntax.

const response = await fetch(URL, { headers })
console.log(response.headers.get('link'))
// ------
// OUTPUT
// ------
// <https://api.github.com/organizations/105071744/repos?page=2>; rel="next", <https://api.github.com/organizations/105071744/repos?page=16>; rel="last"

Thought

This felt like a weird “gotcha” to me. It didn’t take too long to realize there was a gap in my understanding and just looked it up. The search engine AI summary gave this which was enough to clear up the issue for me:

The response.headers property in the Fetch API returns a Headers object that contains the response headers, which are key-value pairs providing metadata about the response.
 This object is not a standard JavaScript object but has methods similar to a Map, allowing you to retrieve individual headers by name or iterate over them.

To access a specific header value, use the get() method. For example, response.headers.get('Content-Type') retrieves the value of the Content-Type header.
 To iterate over all headers, you can use a for...of loop with the entries() method, which returns an iterator over key-value pairs.
 This is necessary because directly logging response.headers may show an empty object, as the headers are not accessible as a plain object.

When making cross-origin requests, there are restrictions on which headers can be accessed due to security policies. By default, only a limited set of standard headers (like Cache-Control, Content-Type, Expires, Last-Modified, and Pragma) are exposed to the client-side script.
 To access custom or non-standard headers (such as Authorization or x-total-count), the server must explicitly expose them by including the Access-Control-Expose-Headers response header.
 For instance, setting Access-Control-Expose-Headers: Authorization allows the client to read the Authorization header.

The Headers object also provides methods like forEach() to iterate over headers, which is useful for older browsers that may not support ES2015 iterators.
 This approach is recommended for ensuring backward compatibility.

But just a reminder that it’s not a normal javascript object and is more like an instance of a class with methods to retrieve values.