What Are HTTP Headers? A Complete Guide
Every page you load and every API call your app makes carries a set of HTTP headers alongside the actual content. They decide how the two sides understand each other, how fast the web feels, and how safe a site is. This guide explains what headers are, how they are structured, the ones you meet most often, and why the security headers are the group most sites get wrong.
What an HTTP header is#
When your browser talks to a server, the two of them exchange more than just the page. Alongside the content itself, they pass a set of notes about that content and about each other, and those notes are HTTP headers. A header is a single piece of metadata, which simply means data about the data, expressed as a name and a value. HTTP, the Hypertext Transfer Protocol, is the language browsers and servers use to move web content around, and headers are the part of that language that carries settings rather than substance.
A useful way to picture it is a short, structured conversation that happens before and around the real payload. The client opens by saying what it wants, which formats it can read, which languages it prefers, and who it is. The server answers with what it is sending back, how long that answer can be reused, whether the connection should stay encrypted, and dozens of other details. None of that is the web page. It is the agreement about how to handle the web page. Get the agreement right and everything works quietly. Get it wrong and pages render as raw text, caches serve stale content, or a site sits wide open to attacks that a single header would have stopped.
Headers are also how HTTP stays flexible. The protocol keeps the request and response format fixed and simple, then lets headers extend it endlessly. New capabilities, from compression to modern security controls, arrive as new headers rather than as changes to HTTP itself. That is why learning headers is really learning how the web negotiates almost everything, and why the same knowledge underpins performance work, debugging, and security hardening.
The authoritative definition lives in RFC 9110, the specification that describes HTTP semantics and refers to these as header fields. Throughout this guide the words header and header field mean the same thing. RFC 9110 replaced a stack of older documents in 2022, so it is the current reference, and its terminology is what you will see in modern tooling and browser documentation.
The anatomy of a header#
Each header is one line with a field name, a colon, and a value, written as Name: value. For example Content-Type: application/json has the field name Content-Type and the value application/json. Field names are case insensitive, so Content-Type, content-type, and CONTENT-TYPE all name the same field. Values are not always so forgiving, since many of them carry URLs, tokens, or filenames where the exact characters matter.
A single field can hold several values at once, usually as a comma separated list. Accept-Encoding: gzip, deflate, br offers three compression formats in one line, and the server picks whichever it supports. Some fields add small parameters after a semicolon to refine the main value, which is how Content-Type: text/html; charset=UTF-8 both names the format and states the character set. These conventions repeat across most headers, so once you can read one you can read almost all of them.
Here is a complete raw request as it actually travels over the wire. The first line is the request line, which names the method, the path, and the HTTP version. Every line after it is a header, and a single blank line signals the end of the headers.
# A real GET request, exactly as it travels to the server. # The first line is the request line, then one header per line, # then a blank line that marks the end of the headers. GET /articles/http-headers HTTP/1.1 Host: www.example.com User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-US,en;q=0.9 Accept-Encoding: gzip, deflate, br Referer: https://www.google.com/ Cookie: session_id=8f3b2a; theme=dark # A GET request has no body, so nothing follows the blank line.
The server replies in a mirror image structure. The first line is the status line, which reports the HTTP version and a status code such as 200 OK. Then come the response headers, then a blank line, then the body, which is the page or data itself.
# The server's reply. The first line is the status line, # then the response headers, then a blank line, then the body. HTTP/1.1 200 OK Date: Mon, 21 Sep 2026 14:03:11 GMT Server: nginx Content-Type: text/html; charset=UTF-8 Content-Length: 5320 Cache-Control: max-age=3600, public ETag: "3f8b2c1a" Set-Cookie: session_id=8f3b2a; HttpOnly; Secure; SameSite=Lax Strict-Transport-Security: max-age=63072000; includeSubDomains Content-Security-Policy: default-src 'self' <!DOCTYPE html> <html> <!-- the page itself begins here, after the blank line --> </html>
That blank line matters more than it looks. It is the only boundary between the headers and the body, which is why a stray blank line or a malformed header can break a response entirely. Modern versions of HTTP change how this is encoded. HTTP/2 and HTTP/3 compress the fields and send names in lowercase for efficiency, but the meaning is identical to the plain text form above, and that plain text form remains the clearest way to learn what headers do.
The request and response cycle#
Every interaction on the web is a round trip. The client sends a request, the server sends back a response, and headers ride along in both directions. In the request, the headers sit at the top, above any body the client is sending, such as form data or a JSON payload on a POST. In the response, they sit at the top too, above the page or data the server returns. In both cases the headers come first and the body follows the blank line, so a recipient can read all the instructions before it has to deal with the content.
This ordering is deliberate. A server can look at request headers like Accept and Authorization and decide how to respond before it reads or even accepts the body. A browser can look at response headers like Content-Type and Content-Security-Policy and set up how it will handle the body before a single byte of it arrives. Headers are the setup, and the body is the payload that setup describes.
It helps to remember that HTTP is stateless. Each request stands on its own, and the server does not automatically remember anything about the last one. Headers are how state gets carried forward anyway. A Set-Cookie response header asks the browser to store a value, and the matching Cookie request header sends it back on the next request, which is how a stateless protocol manages to keep you logged in. The same pattern, a server hint followed by a client echo, shows up again in caching and in cross origin negotiation later in this guide.
The four kinds of header fields#
Headers are easier to reason about when you sort them by what they describe. RFC 9110 groups them into four roles, and a single header always belongs to one of them.
- Request fields are sent by the client and describe the request or the client itself, such as
Host,User-Agent,Accept, andAuthorization. - Response fields are sent by the server and describe the response or the server itself, such as
Server,Set-Cookie, andWWW-Authenticate. - Representation fields describe the format of the content so the other side can interpret it, such as
Content-Type,Content-Encoding,Content-Language, andETag. These can appear on both requests and responses because either side may send content. - Payload fields describe the message body as it was actually transferred, such as
Content-Length,Content-Range, andTransfer-Encoding.
The distinction between representation and payload is subtle but worth holding onto. A representation field describes the content in its natural form, while a payload field describes how that content was packaged for this one transfer. A gzip compressed HTML page has a representation of HTML and a payload that reflects the compressed bytes on the wire.
If you learned this from older documentation
Earlier specifications talked about general headers, which applied to both requests and responses, and entity headers, which described the body. RFC 9110 retired the entity naming in favor of representation and payload, and it dropped the general grouping in favor of simply stating where each field is allowed. If a tutorial mentions entity headers, it means what modern docs call representation and payload fields.
Common request headers#
These are the request headers you will meet most often, each with a real example of how it appears on the wire.
Hostnames the domain the request is for, for exampleHost: www.example.com, which lets a single server answer for many sites on one IP address. It is the one header HTTP/1.1 requires on every request.User-Agentidentifies the client software, such as a browser and operating system string likeMozilla/5.0 ... Chrome/128. Servers read it to adapt responses and analytics tools use it to count browsers, though a client can set it to anything.Acceptlists the media types the client can handle, such asAccept: text/html,application/json;q=0.9, so the server can choose the best format to return.Accept-Languagestates the human languages the user prefers, likeAccept-Language: en-US,en;q=0.9, so a multilingual site can serve the right translation.Accept-Encodingadvertises the compression formats the client understands, such asAccept-Encoding: gzip, br, letting the server shrink the response before sending it.Authorizationcarries credentials that prove who is calling, most often a bearer token likeAuthorization: Bearer eyJhbGc.... It is how a protected API knows a request is allowed.Cookiereturns the cookies the browser stored earlier, such asCookie: session_id=8f3b2a, which is how a stateless protocol remembers you.Content-Typeon a request tells the server how to read the body being sent, for exampleContent-Type: application/jsonon a POST. It is a representation field, so it appears on responses too.Referer, a long standing misspelling of referrer that is now baked into the standard, names the page the request came from, which analytics and some checks rely on.Originnames just the scheme and host a request started from, likeOrigin: https://app.example.com, and it is central to how the browser and server negotiate cross origin requests.If-None-MatchandIf-Modified-Sincemake a request conditional. The browser sends the version tag or timestamp it already holds, and the server can reply with a small304 Not Modifiedinstead of the whole resource when nothing has changed.
Common response headers#
On the way back, the server sends its own set. These are the response headers you will see on almost every page.
Content-Typetells the browser how to interpret the body, such astext/html; charset=UTF-8orapplication/json. Getting it wrong is a common cause of a page that shows up as raw text.Content-Lengthgives the size of the body in bytes so the client knows when it has received the whole thing.Cache-Controlsets the caching rules for the response, for examplemax-age=3600, public, and it is the main lever for how long browsers and proxies may reuse a copy.ETagis a short fingerprint of the current version of a resource, likeETag: "3f8b2c1a", which the browser later echoes back to check whether its cached copy is still fresh.Set-Cookieasks the browser to store a cookie, such asSet-Cookie: session_id=8f3b2a; HttpOnly; Secure; SameSite=Lax, and its attributes decide how safely that cookie behaves.Locationtells the client where to go next and pairs with a redirect status such as301or302, for exampleLocation: https://www.example.com/new-path.Servernames the software answering the request, such asServer: nginx. Revealing detailed version numbers here is a small information leak worth trimming.Access-Control-Allow-Origindeclares which origins may read the response in a browser, for exampleAccess-Control-Allow-Origin: https://app.example.com, and it is the core of CORS.Strict-Transport-Securitytells the browser to only ever connect over HTTPS going forward, such asmax-age=63072000; includeSubDomains.
Quick reference table#
Here are twelve of the headers you will run into most, with the role each one plays and what it does in one line. Keep it handy while you read the rest of the guide.
| Header | Type | What it does |
|---|---|---|
| Host | Request | Names the domain the request is for, so one server can host many sites. |
| User-Agent | Request | Identifies the client software making the request. |
| Accept | Request | Lists the content types the client can handle. |
| Authorization | Request | Carries credentials that prove who the client is. |
| Cookie | Request | Sends stored cookies back to the server on each request. |
| Content-Type | Representation | States the media type of the body, such as application/json. |
| Cache-Control | Response | Sets caching rules such as max-age and no-store. |
| ETag | Response | A version fingerprint used to revalidate a cached copy. |
| Set-Cookie | Response | Asks the browser to store a cookie and how to guard it. |
| Location | Response | Points the client to a new URL for a redirect. |
| Access-Control-Allow-Origin | Response | Declares which origins may read the response (CORS). |
| Strict-Transport-Security | Response | Forces future visits to use HTTPS. |
What headers actually power#
Individual headers are easy to memorize. What makes them worth understanding is the systems they build when they work together. Four of those systems account for most of what headers do day to day.
Content negotiation
Content negotiation is how a client and server agree on the best form of a resource. The client sends Accept, Accept-Language, and Accept-Encoding to state its preferences, and the server picks accordingly. Preferences are ranked with quality values, written as a q parameter from 0 to 1. In Accept: text/html,application/xml;q=0.9,*/*;q=0.8 the client says it most wants HTML, will take XML as a second choice, and will accept anything else if it must. The server reads that ranking and returns HTML when it can.
Caching and conditional requests
Caching is why the web feels fast, and Cache-Control is the header that governs it. Directives inside it set the policy. max-age=3600 allows a copy to be reused for an hour, no-store forbids caching entirely for sensitive data, and private lets only the user's own browser cache the response, not a shared proxy in between. When a cached copy expires, the browser does not always refetch the whole thing. Instead it makes a conditional request using the ETag it was given, sending it back in If-None-Match. If the resource has not changed, the server answers with a compact 304 Not Modified and no body at all.
# The first response hands the browser a version tag (ETag). HTTP/1.1 200 OK ETag: "3f8b2c1a" Cache-Control: max-age=0, must-revalidate # Next visit, the browser asks: only send it if it has changed. GET /styles/app.css HTTP/1.1 If-None-Match: "3f8b2c1a" # Nothing changed, so the server skips the body entirely. # This is a tiny, fast reply instead of the full file. HTTP/1.1 304 Not Modified ETag: "3f8b2c1a"
Cross origin access with CORS
By default a browser blocks a page on one origin from reading a response fetched from another origin, where an origin is the combination of scheme, host, and port. Cross Origin Resource Sharing, or CORS, is the header based system that safely relaxes that rule. A server opts in by sending Access-Control-Allow-Origin naming which origins are allowed. For requests that can change data, the browser first sends a preflight, a small OPTIONS request that asks in advance whether the real request is permitted, and the server answers with the methods and headers it will accept before the real call is made. If you are configuring this, the CORS configuration guide walks through getting the policy right.
Cookies and sessions
Cookies are the classic use of the server hint and client echo pattern. A server sends Set-Cookie to store a value, and the browser returns it in the Cookie header on every later request to that site. The attributes on Set-Cookie are where the security lives. HttpOnly hides the cookie from JavaScript so a script injection cannot steal it, Secure stops it being sent over plain HTTP, and SameSite controls whether it rides along on requests from other sites, which blunts cross site request forgery. Our guide to securing cookies covers each attribute in depth.
Security headers, the group most sites get wrong#
Security headers are response headers that instruct the browser to enforce protections against common attacks. They are the highest leverage headers you can set, because a handful of them shut down whole categories of exploit, yet they are also the ones sites most often skip, since a page works perfectly well without them. Here is what the main ones do.
Content-Security-Policyrestricts where scripts, styles, images, and other resources are allowed to load from, which is the single strongest defense against cross site scripting, the injection of hostile scripts into your pages. See the deep dive on Content Security Policy.Strict-Transport-Securityforces browsers to use HTTPS for every future visit, which shuts down attacks that try to downgrade a connection to plain HTTP. Learn the details in what is HSTS.X-Content-Type-Options: nosniffstops the browser from guessing a file type against the declaredContent-Type, which closes off tricks that get a browser to run a file as script.X-Frame-Options, and the modernframe-ancestorsdirective in CSP, prevent your pages being loaded inside another site's frame, which is how clickjacking tricks users into clicking things they cannot see.Referrer-Policycontrols how much of the originating URL is shared when a user follows a link, andPermissions-Policycontrols which browser features, such as the camera or geolocation, a page and its embeds may use.
A missing security header throws no error
Unlike a broken Content-Type that makes a page look wrong, an absent security header produces no visible symptom. The site loads, tests pass, and the gap stays quiet until someone exploits it. That is exactly why these headers get skipped, and why checking for them on a schedule matters more than checking them once.
For a compact list of every security header with recommended values you can copy, keep the security headers cheat sheet open. When you are ready to confirm what a live site is actually sending, the walkthrough on how to check security headers shows you how to read them and spot the gaps.
Custom headers#
Nothing stops you inventing your own headers, and applications do it all the time to pass information that no standard field covers. For years the convention was to prefix a nonstandard header with X- to mark it as experimental, which is where names like X-Frame-Options came from. That convention turned out to cause more trouble than it solved, because experimental headers often became standard and were then stuck with a misleading prefix forever.
In 2012, RFC 6648 formally deprecated the mandatory X- prefix, advising authors to simply choose a clear name without it. You will still see the prefix everywhere, since it is deeply entrenched, but new headers no longer need it. Common custom headers you might meet include X-Request-ID, which tags a request with a unique identifier so it can be traced through logs, and X-RateLimit-Remaining, which tells an API client how many calls it has left before it is throttled. Treat any custom header you receive as untrusted input, because the sender can set it to anything.
How to view the headers on any site#
The fastest way to see headers in a browser is the developer tools. Open them with F12 or the right click Inspect menu, switch to the Network tab, and reload the page. Click any request in the list and you will see two panels, Request Headers and Response Headers, showing exactly what your browser sent and what the server sent back. It is the clearest window into everything covered in this guide, and it updates live as you navigate.
From a terminal, curl does the same job and is easy to script. The -I flag fetches only the response headers, and -v shows the request headers your client sent as well.
# -I fetches only the response headers (a HEAD request). curl -I https://www.example.com # Sample output. Note HTTP/2 lowercases every field name. HTTP/2 200 content-type: text/html; charset=UTF-8 cache-control: max-age=3600, public strict-transport-security: max-age=63072000; includeSubDomains content-security-policy: default-src 'self' x-content-type-options: nosniff # Use -v to also see the request headers your client sent. curl -v https://www.example.com
Reading headers by hand is perfect for inspecting one page. When the question is which protective headers a whole site is missing, a security scan is far quicker, because it checks every response and flags the gaps in one pass rather than making you eyeball each one. You can scan your site's headers for free and see exactly which of the protective headers above are present, which are misconfigured, and which are absent.
Best practices and common mistakes#
Practices worth adopting
- Set security headers on every response, not just the homepage, since an attacker will probe the one path you forgot.
- Always send a correct
Content-Typewith an explicit charset, so browsers never have to guess how to render your content. - Use caching deliberately, with a long
max-agefor static assets whose filenames include a content hash, andno-storefor anything sensitive. - Trim revealing values from
ServerandX-Powered-By, which mostly help someone fingerprint your stack. - Validate anything you act on from a request header, and keep the set of headers you trust as small as possible.
Mistakes to avoid
- Trusting client set headers such as
User-Agent,Referer, orX-Forwarded-Forfor authentication or access decisions. A client can set any of them to any value. - Assuming your framework added security headers for you. Most add none by default, so an untouched app usually ships with the whole set missing.
- Sending
Access-Control-Allow-Origin: *on endpoints that return private data, which hands that data to any site that asks for it. - Leaving
Cache-Controlunset on authenticated pages, which lets a shared cache or the back button serve one user's content to another.
FAQ#
What are HTTP headers?
HTTP headers are name and value pairs that travel with every web request and response and carry information about the message rather than the message itself. A request header tells the server who is asking and what the client can handle, such as Host, User-Agent, and Accept. A response header tells the browser how to treat what it received, such as Content-Type, Cache-Control, and Set-Cookie. Headers power content negotiation, caching, cookies, cross origin access, and the security controls that protect a site, all without changing the actual page or data being sent.
What is the difference between a request header and a response header?
A request header is sent by the client, usually a browser, and gives the server context about the request and about the client, for example which domain is wanted with Host, what formats are acceptable with Accept, and what credentials are attached with Authorization. A response header is sent back by the server and tells the client how to handle the reply, for example the format of the body with Content-Type, how long it may be cached with Cache-Control, and which cookies to store with Set-Cookie. Some fields, such as Content-Type, can appear in both directions because they describe the body that either side is sending.
Are HTTP header names case sensitive?
HTTP header field names are case insensitive, so Content-Type, content-type, and CONTENT-TYPE all refer to the same field, and RFC 9110 confirms this. HTTP/2 and HTTP/3 go further and require field names to be sent in lowercase on the wire. Header values are a different matter. Some values are case insensitive tokens, but many carry data such as URLs, tokens, or filenames where case can matter, so you should never assume a value can be freely changed in case.
What are security headers?
Security headers are a subset of HTTP response headers that instruct the browser to enforce protections against common attacks. Content-Security-Policy limits where scripts and other resources may load from to reduce cross site scripting. Strict-Transport-Security forces HTTPS. X-Content-Type-Options stops the browser guessing file types. X-Frame-Options and the CSP frame-ancestors directive block clickjacking. Referrer-Policy and Permissions-Policy control what information and browser features pages may use. A site works without them, so a missing security header produces no error and the gap stays invisible until it is exploited.
How do I see the HTTP headers for a website?
Open your browser developer tools, go to the Network tab, reload the page, click the request, and read the Request Headers and Response Headers panels. From a terminal you can run curl -I on a URL to print only the response headers, or curl -v to also see the request headers your client sent. To check specifically which protective headers a site is missing, a security scan is faster than reading them by hand, because it inspects every response and flags the gaps for you.
References
Related articles
See Which Headers Your Site Is Missing
Reading headers one page at a time is slow. Run a free scan to see every protective header your site sends, which are misconfigured, and which are absent, across your whole domain.