Working with Response Headers

1. Setting Single Header

APIDetail
res.set("X-Foo", "bar")Express style
res.header("X-Foo", "bar")Alias
res.setHeader("X-Foo", "bar")Node native

2. Setting Multiple Headers

Example: Object form

res.set({
  "Cache-Control": "no-store",
  "X-Request-Id": req.id,
  "X-Frame-Options": "DENY"
});

3. Appending Headers

HeaderMulti-value reason
Set-CookieMultiple cookies
VaryMultiple varying fields
LinkHATEOAS / pagination
WWW-AuthenticateMultiple auth schemes

4. Getting Response Header

Example: Read what was set

res.set("X-Cache", "HIT");
console.log(res.get("X-Cache"));  // "HIT"

5. Removing Headers

Example: Strip framework fingerprint

app.disable("x-powered-by");
// Or per-response
res.removeHeader("X-Powered-By");
res.removeHeader("ETag");
APINotes
res.removeHeader(name)Node native; only works before res.end()

6. Setting Content-Type

Example: type() shortcuts

res.type("json");        // application/json
res.type("html");        // text/html; charset=utf-8
res.type(".png");        // image/png
res.type("application/vnd.api+json");

7. Setting Cache-Control Headers

DirectiveEffect
no-storeNever cache
no-cacheCache but revalidate every time
privateBrowser cache only (not CDN)
public, max-age=NCache for N seconds anywhere
s-maxage=NCDN-only TTL
immutableAsset never changes (hashed filenames)
stale-while-revalidate=NServe stale while refreshing

Example: Asset vs API caching

// Hashed asset
res.set("Cache-Control", "public, max-age=31536000, immutable");
// API: no caching
res.set("Cache-Control", "no-store");

8. Setting Custom Headers

Example: Tracing

res.set({
  "X-Request-Id": req.id,
  "X-Response-Time": `${Date.now() - req.startTime}ms`,
  "X-API-Version": "v2"
});
Note: Custom headers should be prefixed with X- only by convention. Modern guidance (RFC 6648) discourages X- for new standardized headers but it's still common for app-specific.

9. Using Location Header

Example: 201 with Location

app.post("/users", async (req, res) => {
  const user = await db.users.create(req.body);
  res.status(201).location(`/users/${user.id}`).json(user);
});
StatusLocation semantic
201 CreatedURL of new resource
301/302/303/307/308Redirect target

10. Setting Vary Header

Example: Content negotiation

res.vary("Accept-Encoding");      // gzip vs identity
res.vary("Accept-Language");      // i18n response varies by language
res.vary("Origin");                // CORS responses
Header to VaryWhen
AcceptDifferent formats (HTML/JSON)
Accept-EncodingCompressed vs not
Accept-LanguageLocalized content
OriginCORS
CookiePer-user content