Request headers carry the client's context: what formats it accepts, who the user is, what it has cached. Names are case-insensitive (HTTP/2 requires lowercase). fetch(url, { headers }) can add your own, but the browser controls forbidden headers such as Host, Cookie, Referer, Origin and any Sec- or Proxy- name, so scripts cannot forge them.
| Header | Purpose and example |
|---|---|
| Host, User-Agent | Target host (required in HTTP/1.1); browser ID |
| Accept, Accept-Language | Preferred formats and languages |
| Accept-Encoding | Decodable compression: gzip, deflate, br, zstd |
| Authorization, Cookie | Credentials: Bearer <token>; stored cookies |
| Content-Type, Content-Length | Format and size of the request body |
| If-None-Match, If-Modified-Since | Conditional request; may get 304 |
| Range | Part of a resource: bytes=0-1023 |
| Origin, Referer | Requesting origin; referring page (Security) |
| Sec-Fetch-Site, Sec-Fetch-Dest | Fetch metadata: cross-site? script or page? |
| Forwarded, X-Forwarded-For | Client IP as recorded by proxies |
Fetch metadata lets a server reject a suspicious request before doing any work, such as a cross-site POST that none of your pages would send. DNT is deprecated in favor of Sec-GPC (Global Privacy Control), Pragma in favor of Cache-Control, and the standard Forwarded (RFC 7239) replaces X-Forwarded-For. Trust either only when your own proxy sets it.