1. Library
  2. Computer Networks
  3. Http and the Web
  4. Headers

Updated 9 hours ago

When you request something from a server, the response comes in two parts. The body is what you asked for—the HTML, the image, the JSON data. The headers are the server telling you what it just handed you and what you should do with it.

Think of it like receiving a package. The package contents are the body. The headers are the handling instructions attached to the outside: "Fragile." "Refrigerate after opening." "This is a gift—don't show the price."

The Questions Every Response Answers

Every HTTP response implicitly answers a set of questions:

What is this? The Content-Type header tells the browser whether it's looking at HTML, JSON, an image, or a PDF. Without it, the browser receives a stream of bytes with no idea how to interpret them.

How big is it? Content-Length lets browsers show download progress and know when they've received everything.

Is it compressed? Content-Encoding tells browsers to decompress the response before using it.

Can I cache it? Cache-Control and related headers determine whether browsers and CDNs can store copies, and for how long.

What security rules apply? Headers like Content-Security-Policy and Strict-Transport-Security tell browsers what to block and what to enforce.

Should I store any cookies? Set-Cookie headers create persistent state between requests.

Should I go somewhere else? Location headers (with redirect status codes) tell browsers to request a different URL.

Every response header is an answer to one of these questions.

Content Headers: What You're Getting

Content-Type

The most fundamental response header. It tells the browser how to interpret the bytes it just received:

Content-Type: text/html; charset=utf-8

Without Content-Type, the browser receives a stream of bytes and has no idea what it's looking at. Is this HTML to render? JSON to parse? An image to display? The bytes themselves don't say.

Common values:

  • text/html — render as a webpage
  • application/json — parse as structured data
  • image/png — display as an image
  • application/pdf — display or download as PDF
  • text/css — apply as stylesheet
  • application/javascript — execute as code

The charset=utf-8 part specifies character encoding. Get this wrong and text displays as garbled nonsense.

Content-Length

The size of the response body in bytes:

Content-Length: 348

This header enables progress bars. Without knowing the total size, browsers can only show "loading..." with no indication of how much remains. It also lets clients verify they received the complete response.

Content-Encoding

Indicates compression applied to the response:

Content-Encoding: gzip

The browser automatically decompresses before processing. Common encodings:

  • gzip — widely supported, good compression
  • br — Brotli, better compression ratios for text

Compression can reduce transfer size by 70-90% for text content like HTML, CSS, and JavaScript. The bytes on the wire are compressed; the browser inflates them back to the original.

Content-Disposition

Controls whether content displays inline or triggers a download:

Content-Disposition: attachment; filename="report.pdf"

With inline (the default for most content), browsers display the response directly. With attachment, they prompt the user to save a file. The filename parameter suggests what to call it.

This is how a server can send identical bytes but have one URL display a PDF in-browser while another triggers a download dialog.

Caching Headers: Can I Keep a Copy?

Cache-Control

The primary header controlling how responses can be cached:

Cache-Control: public, max-age=3600, must-revalidate

The directives that matter:

public — any cache can store this: CDNs, proxies, browsers. Use for static assets like images and scripts.

private — only the user's browser can cache this, not shared caches. Use for personalized content.

max-age=3600 — the response is fresh for 3600 seconds (one hour). During this window, caches can serve it without checking with the server.

no-cache — caches must validate with the server before using a stored copy. The cache keeps the response but always checks if it's still valid.

no-store — don't cache at all. The response should never be written to disk. Use for sensitive data.

must-revalidate — once stale, caches must revalidate. They cannot serve stale content even if the server is unreachable.

ETag

A fingerprint that uniquely identifies a specific version of the resource:

ETag: "a1b2c3d4e5f6"

Here's how ETags make caching efficient: The browser caches the response along with its ETag. Later, when the cache entry is stale, the browser requests the resource again but includes the ETag in an If-None-Match header. If the content hasn't changed, the server responds with 304 Not Modified—no body, just confirmation that the cached version is still valid.

This turns a potentially large download into a tiny validation check.

Last-Modified

When the resource was last changed:

Last-Modified: Wed, 21 Oct 2024 07:28:00 GMT

Similar to ETag but based on timestamps rather than content fingerprints. The browser sends this back in If-Modified-Since headers for validation. Less precise than ETags (only one-second granularity) but simpler to implement.

Cookie Headers: Remember This

Set-Cookie

Instructs the browser to store a cookie:

Set-Cookie: sessionId=abc123; Secure; HttpOnly; SameSite=Strict; Max-Age=3600

The cookie value (sessionId=abc123) is the data. The attributes control behavior:

Secure — only send this cookie over HTTPS. Prevents interception on insecure connections.

HttpOnly — JavaScript cannot access this cookie. Protects session tokens from XSS attacks that try to steal cookies via document.cookie.

SameSite — controls when cookies are sent with cross-site requests:

  • Strict — never send cross-site (strongest protection)
  • Lax — send on top-level navigations but not embedded requests
  • None — always send (requires Secure; used for legitimate cross-site scenarios)

Max-Age — how many seconds until the cookie expires. Without this or Expires, the cookie is a "session cookie" that disappears when the browser closes.

A single response can include multiple Set-Cookie headers to set multiple cookies.

Security Headers: Enforce These Rules

Strict-Transport-Security (HSTS)

Tells browsers to always use HTTPS:

Strict-Transport-Security: max-age=31536000; includeSubDomains

Once a browser receives this header, it automatically converts any HTTP request to HTTPS for the specified duration. Even if a user types http:// or clicks an HTTP link, the browser upgrades to HTTPS before sending anything.

This prevents attackers from intercepting the initial HTTP request before a redirect to HTTPS could happen.

Content-Security-Policy (CSP)

Defines which resources the page is allowed to load:

Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted.cdn.com

CSP creates a whitelist. Any resource not explicitly allowed gets blocked. This is powerful protection against XSS attacks—even if an attacker injects a malicious script tag, it won't execute if the source isn't whitelisted.

The directives specify source rules for different resource types: script-src for JavaScript, img-src for images, style-src for CSS, and so on. default-src sets the fallback for anything not explicitly specified.

X-Content-Type-Options

X-Content-Type-Options: nosniff

Prevents browsers from "sniffing" the content type. Without this, browsers might ignore the Content-Type header and guess based on the content itself. If user-uploaded content gets misinterpreted as executable JavaScript, that's a security hole. This header forces browsers to trust Content-Type.

X-Frame-Options

Controls whether the page can be embedded in a frame:

X-Frame-Options: DENY
  • DENY — cannot be framed by anyone
  • SAMEORIGIN — can only be framed by pages from the same origin

This prevents clickjacking attacks where a malicious site embeds your page in an invisible iframe and tricks users into clicking on it.

CORS Headers: Cross-Origin Access

When JavaScript on one origin tries to access a resource from a different origin, browsers block it by default. CORS headers grant explicit permission.

Access-Control-Allow-Origin

The fundamental CORS header:

Access-Control-Allow-Origin: https://www.example.com

This says: "JavaScript from https://www.example.com is allowed to read this response." Without this header, the browser fetches the response but refuses to let JavaScript access it.

The wildcard * allows any origin, but cannot be used with credentials (cookies).

Access-Control-Allow-Methods

Which HTTP methods are allowed for cross-origin requests:

Access-Control-Allow-Methods: GET, POST, PUT, DELETE

This appears in responses to preflight requests, telling browsers which methods the actual request can use.

Access-Control-Allow-Headers

Which request headers are permitted:

Access-Control-Allow-Headers: Content-Type, Authorization

Custom headers require explicit permission. Without this, browsers reject cross-origin requests that include headers beyond the basic safe list.

Access-Control-Max-Age

How long browsers can cache preflight results:

Access-Control-Max-Age: 86400

Preflight requests add latency. Caching the results for 86400 seconds (one day) means browsers don't need to preflight every single cross-origin request.

Redirect Headers

Location

Used with 3xx status codes to specify where to go:

Location: https://www.example.com/new-page

When a response has a redirect status code (301, 302, 307, 308), the browser automatically requests the URL in the Location header. The redirect status code determines whether the browser can cache the redirect and whether it should change the HTTP method.

Connection Headers

Transfer-Encoding

How the message body is transferred:

Transfer-Encoding: chunked

Chunked encoding allows servers to start sending before knowing the total content length. Each chunk arrives with its size, and a zero-size chunk signals the end. This enables streaming responses and dynamically generated content where the final size isn't known upfront.

With chunked encoding, there's no Content-Length header—the chunks themselves define the boundaries.

Server Information

Date

When the response was generated:

Date: Wed, 21 Oct 2024 07:28:00 GMT

Useful for debugging and calculating cache freshness.

Age

How long the response has been sitting in a cache:

Age: 3600

CDNs and proxies add this header. If Cache-Control says max-age=7200 and Age says 3600, the response has one hour of freshness remaining.

Server

Identifies the server software:

Server: nginx/1.18.0

Informational, but often omitted in production because revealing exact versions helps attackers target known vulnerabilities.

Frequently Asked Questions About Response Headers

Was this page helpful?

😔
🤨
😃