Syllabus Lesson 34 of 156 · HTTP from the inside
HTTP from the inside

Headers

Why it matters A single request can tell a third party who you are (a cookie), where you were (the page you came from), roughly where you live (your language) and what device you use. Knowing exactly which headers carry what lets you audit a site, write a blocker, and check that the privacy signals you switched on are really being sent.

Go back to the letter picture. The headers are the notes written on the envelope. Most are harmless sorting instructions, but some are like writing your name, your last address and your home town on the outside, where everyone who handles the letter can read them. This lesson is about spotting those.

The headers that matter for privacy

HeaderWhat it saysWhy a tracker cares
Cookiethe cookies the browser has for this serveran ID that recognises you on every visit
Refererthe page that made the requestwhere you were when the request was sent
User-Agentbrowser, version and systemone ingredient of a fingerprint
Accept-Languageyour preferred languagesa fingerprint ingredient and a hint about where you live
Originthe site that sent a form or a fetchwhich site you were on
Sec-Fetch-Sitesame-origin, same-site, cross-site or nonetells a server it is being called from another site (useful for defence too)
Sec-GPC1 when Global Privacy Control is ona request not to sell or share your data
DNT1 for Do Not Trackan older request not to be tracked

Referer: the header naming the page a request came from. It is spelled with one r in the middle, a typo from the 1990s that stuck. Modern browsers by default send only the site's origin (for example https://shopa.example/) to other sites, not the full address (checked October 2026).

Global Privacy Control (GPC): a browser setting that adds Sec-GPC: 1 to every request, asking sites not to sell or share your personal data. Some browsers turn it on by default or in a privacy mode; it is a request, not a block.

Do Not Track is the older cousin of GPC. Very few sites ever honoured it and some browsers have dropped the setting, but some still send it (checked October 2026), so your code should still read it.

Two rules that trip people up

Names ignore case. Cookie, cookie and COOKIE are the same header. HTTP/2 and HTTP/3 even send every name in lower case. So compare names with .lower() and keep values exactly as written.

Headers can repeat. A response often carries several Set-Cookie lines, one per cookie. That is why the last lesson kept a list of pairs instead of a dict, which would quietly drop all but one. For most headers, HTTP says repeated values can be joined with ", ", so we do that when we report them.

A common misconception

"Only cookies identify me." A cookie is the most direct ID, but User-Agent plus Accept-Language plus your IP address already narrows you down a lot, and the Referer tells the tracker where you were even with no cookie at all. That is why a blocker cares about whole requests, not only cookies.

Try it In DevTools, Network tab, click a request to an image or script on another site and read its Request Headers. Is there a Cookie? What does Referer say, the full page address or only the site? Then search the headers for Sec-GPC to see whether your browser sends it.

In other languages

JavaScript:

// In a service worker or server, the Headers object ignores case for you:
const ua = request.headers.get("user-agent");      // same as "User-Agent"
const gpc = request.headers.get("Sec-GPC") === "1";
const cookies = request.headers.get("Cookie") ?? "";

PHP:

// PHP puts request headers in $_SERVER, upper-cased with HTTP_ in front
$referer = $_SERVER['HTTP_REFERER'] ?? null;
$gpc = ($_SERVER['HTTP_SEC_GPC'] ?? '') === '1';

C#:

// ASP.NET Core: header lookups ignore case
var gpc = Request.Headers["Sec-GPC"] == "1";
var lang = Request.Headers.AcceptLanguage.ToString();

Recap

  • Cookie, Referer, User-Agent, Accept-Language, Origin and Sec-Fetch-Site are the headers that tell a server most about you.
  • Sec-GPC: 1 and DNT: 1 are your browser asking not to be tracked or sold; only the value 1 counts.
  • Header names ignore case and headers can repeat, so look them up with lower case and collect every value.
Your turn

header_lines(raw) is given. Write get_all(raw, name): every value of that header in order, ignoring case ([] if missing). Write tracking_headers(raw): a dict of lower-case name to value for each name in TRACKING_HEADERS that appears, with repeated values joined by ", ". Write opt_out_signals(raw): {"gpc": ..., "dnt": ...}, each True only when that header is present with the value "1".

Spotted a problem in this lesson? Report it

Code · runs in your browser
Output