← Back to blog

HTTP Headers: Debug HTTP/2 and HTTP/3 Without Installing Tools

October 17, 2026
HTTP Headers: Debug HTTP/2 and HTTP/3 Without Installing Tools

HTTP headers are the name:value fields that travel with every request and response, defined in RFC 9110 and documented by MDN Web Docs. They tell clients and servers what content is being sent, how long it may be cached, who is allowed to read it, and how the connection should behave. Control them, and you control behavior, caching, and security.


TL;DR:

  • Mark personalized responses private or no-store; otherwise, a shared cache may deliver one person’s account data to another visitor without warning.
  • Use Content Security Policy in report only mode before enforcement, and send HSTS only over HTTPS after confirming the certificate is healthy.
  • When a response changes by language, tell caches to vary by the request language field, and offer a switcher to override browser preferences.
  • No universal header size cap exists; oversized cookie collections can trigger 400 or 502 errors, so keep cookies small and check limits at every layer.
  • HTTP/2 and HTTP/3 lowercase field names and compress header blocks, so use accessors that ignore case and inspect traffic with tools that decode frames.

Pingfloat
Check the Headers Behind Site Health
Pingfloat brings together web tools for SSL checks and network diagnostics, helping developers investigate website security and connection issues.
Explore Pingfloat tools

Table of Contents

1. What are HTTP headers and where do they sit in a message?

Every HTTP/1.1 message follows the same layout: a start-line, zero or more header field lines, an empty line, and an optional body. RFC 9112 defines those framing and parsing rules. In a request, the start-line carries the method, target, and protocol version. In a response, it carries the version and status code. The header section sits between the start-line and the body, and the blank line is the signal that headers have ended.

Headers are extensible name:value pairs. That extensibility is deliberate: RFC 9110 describes HTTP as an extensible protocol where header fields are the main vehicle for adding capabilities without changing the core request-response model. New field names are tracked in the IANA field name registry, which is why you can find an official entry for almost every standard header you meet.

Case is the detail that trips people up. In HTTP/1.x, header names are case-insensitive, so Content-Type and content-type mean the same thing. MDN's header reference notes that HTTP/2 and HTTP/3 send names in lowercase on the wire to support compression and efficient parsing. Header values, by contrast, can be case-sensitive depending on the field, such as a token in an Authorization header.

2. Request, response, representation, and pseudo-headers

Headers are grouped by what they describe rather than by a rigid syntax.

Request headers describe the client and what it wants. Accept lists the media types it can handle, Authorization carries credentials, and Cookie returns stored cookies to the server. They never describe the body of a response, only the intent and context of the request.

Response headers describe the server's answer and how the client should treat it. Content-Type states the media type, Set-Cookie creates a cookie, and Location points to a redirect target.

Representation headers describe the payload itself, whichever direction it travels. Content-Length gives the body size in bytes, and Transfer-Encoding states how the body was framed for transfer. Content-Encoding and Content-Language belong here too, since they describe the representation rather than the exchange.

Pseudo-headers exist only in HTTP/2 and HTTP/3. Names beginning with a colon, such as :method, :path, :scheme, :authority, and :status, carry the information that used to live in the HTTP/1.1 start-line. RFC 9114 confirms HTTP/3 keeps them, even though the transport moves to QUIC. They always come before regular fields, and applications never set them by hand. Your framework or server library fills them in.

3. Which common headers should every developer know?

A short list covers most of what you read in a trace. The first group below is the mental checklist; the numbered steps after it show how a typical exchange unfolds.

  • Host: names the target host (and port). Required in HTTP/1.1, it lets one IP address serve many sites. Example: Host: example.com.
  • User-Agent: identifies the client software. Example: User-Agent: curl. Treat it as a hint, never as proof. Our guide to parsing user agent strings explains why sniffing it is brittle, and our User Agent lookup shows what your own client sends.
  • Accept and Accept-Encoding: declare acceptable formats and compression. Example: Accept: application/json and Accept-Encoding: gzip, br.
  • Content-Type, Content-Length, Transfer-Encoding: describe the body. Use Content-Length when the size is known up front, and Transfer-Encoding: chunked when it is not.
  • Set-Cookie and Cookie: a server sends Set-Cookie: sid=abc123; Secure; HttpOnly; SameSite=Lax, and the browser later returns Cookie: sid=abc123. MDN's cookies guide covers how those attributes limit scope and exposure.
  • Cache-Control, ETag, Last-Modified, Vary: control storage and validation. Example: Cache-Control: max-age=3600 with ETag: "v1-8f3a".
  • Server, Date, Connection: server-supplied context. Server often leaks software versions, so trim or generalize it when you can.

A typical fetch of a page then plays out in four steps:

  1. The client sends GET /pricing with Host, Accept, and Accept-Encoding.
  2. The server answers with a status line plus Content-Type, Content-Encoding, and Cache-Control.
  3. The client stores the response according to those caching fields.
  4. On the next visit, it sends If-None-Match with the stored ETag and may receive a short 304 Not Modified.

4. How do caching headers work, and where do they go wrong?

Cache-Control is the main switch. max-age=N says a response stays fresh for N seconds. no-cache allows storage but forces revalidation before reuse, while no-store forbids storage altogether. public permits shared caches such as CDNs, and private restricts storage to the user's own browser. A safe default for static, versioned assets is public, max-age=31536000, immutable. For HTML that changes, no-cache plus a validator is usually the better starting point.

Validation is where bandwidth savings come from. The server sends ETag: "abc". On the next request the client sends If-None-Match: "abc". If nothing changed, the server replies 304 Not Modified with no body, and the client reuses what it has.

The pitfall that causes real incidents is leaking personalized content. MDN's HTTP caching guide recommends defining cacheability explicitly instead of relying on heuristic caching, and warns that a personalized response not marked private can be stored by a shared cache and served to someone else. If a response contains account data, send Cache-Control: private or no-store. The Cache-Control reference lists every directive and its effect.

5. Which security headers should you check first?

A handful of response headers remove entire classes of risk.

Content-Security-Policy restricts where scripts, styles, and other resources may load from, which limits the damage to cross-site scripting. Start in reporting mode so you can see violations without breaking pages: Content-Security-Policy-Report-Only: default-src 'self'; report-to csp-endpoint. Move to enforcement once the reports are clean.

Strict-Transport-Security tells browsers to use HTTPS only for your host. A sample is Strict-Transport-Security: max-age=N; includeSubDomains. Only send it over HTTPS, and make sure your certificate is healthy first. Our SSL Checker is a fast way to confirm that.

X-Content-Type-Options: nosniff stops browsers from guessing a content type different from the one you declared, which closes off a content-sniffing trick where an uploaded file gets executed as a script.

Round out the checklist with three more:

  • Referrer-Policy controls how much of the URL leaks to other sites.
  • Permissions-Policy limits access to browser features such as the camera or geolocation.
  • Clear-Site-Data instructs the browser to wipe stored data, useful on logout.

For wider organizational context beyond headers, this overview of best practices for securing cloud environments is a useful companion read.

6. How do headers work in HTTP/2 and HTTP/3?

The semantics stay the same, but the wire format changes completely. RFC 9113 describes HTTP/2 headers traveling in HEADERS frames, with large blocks split across CONTINUATION frames. The receiver must reassemble the full field block before it can interpret anything. The block is compressed with HPACK. HTTP/3, per RFC 9114, sends HEADERS frames on QUIC streams and swaps HPACK for QPACK, which avoids head-of-line blocking between streams.

HTTP/2 and HTTP/3 header transport comparison

Compression matters for debugging because raw captures are no longer readable text. You cannot just grep a packet dump for Set-Cookie. Use browser DevTools, curl -v, or a proxy that decodes frames.

Expect lowercase names everywhere. A trace of a modern site shows content-type, not Content-Type, even if your server code or old docs use title case. The practical rule: never write application code that compares header names case-sensitively. Normalize to lowercase or use your framework's case-insensitive accessor, and your code will behave the same across HTTP/1.1, HTTP/2, and HTTP/3.

7. How do you inspect and debug headers?

Start in the browser. Open DevTools, go to the Network tab, click a request, and read the Headers pane. Check the security fields and Cache-Control first, since misconfiguration there is the most common problem.

From the terminal, curl covers most needs:

  • curl -I https://example.com fetches headers only, using a HEAD request.
  • curl -i https://example.com includes headers alongside the body.
  • curl -v https://example.com shows the full exchange, including request headers and protocol negotiation.

When a request fails strangely, capture the raw bytes with tcpdump or ngrep, or route traffic through a local proxy. This matters for malformed requests, where a parser rejects something your tools never show.

Pro Tip: Before reaching for packet capture, run the URL through a no-install header inspector such as our Response Headers Inspector to see cache and security fields at a glance.

8. How are header folding and multiline headers handled?

Header folding is the old practice of splitting one long header value across several lines by starting continuation lines with a space or tab. It was permitted in early HTTP/1.1 but is now obsolete. Modern parsers treat obs-fold as something to reject or normalize, and senders should never produce it. The reason is security: different components unfolded values differently, which opened the door to request smuggling and header injection.

The safe approach is simple. Send each header on one line, and if a value is genuinely long, shorten it or split the information across separate fields. When the same field legitimately repeats, such as multiple Set-Cookie lines, send separate lines instead of folding. For most other fields, repeated lines are equivalent to a single comma-separated list, but Set-Cookie is the notable exception, because its values can contain commas and so must never be merged.

Formatting is also unforgiving in smaller ways. Whitespace between the field name and the colon is invalid, and servers may answer with a 400 error. One parser issue in the httparse project shows how such a stray space can lead to rejected requests, so when a request fails mysteriously, inspect the raw bytes. HTTP/2 and HTTP/3 remove the problem entirely: each field is a discrete name-value pair in a compressed block, so there are no lines to fold. If you see a 400 from a server and want to understand what triggers it, our 400 Bad Request reference walks through the usual causes.

8. How are header folding and multiline headers handled? — overview diagram

9. What limits and size restrictions apply to headers?

The specifications do not set a universal maximum header size, so limits come from implementations. RFC 9110 leaves room for servers to impose their own caps and to respond with an error when a field is too large. In practice, web servers, load balancers, CDNs, and application frameworks each enforce separate limits on the size of a single header line, the total header block, and sometimes the number of fields.

The usual culprit is cookies. Because the browser sends every matching cookie on every request, a long list of tracking or session cookies can quietly inflate the header block until a proxy rejects the request. The symptom is typically a 400 or sometimes a 502 from an intermediary, and it often hits only some users, the ones with the most cookies.

HTTP/2 adds its own wrinkle. Peers can advertise a limit on the size of the header list they will accept, and exceeding it is an error. Compression reduces bytes on the wire, but the limit applies to the uncompressed list, so compression does not save you.

To stay out of trouble:

  • Keep cookies small and scoped to the paths that need them.
  • Store identifiers, not data, in cookies and tokens.
  • Check the limits configured at every layer between client and application.
  • Watch for intermediary errors; our 502 Bad Gateway reference explains how gateway failures show up and how to trace them.

When a request works in one environment but fails in another, header size is worth ruling out early.

10. How do headers drive content negotiation and localization?

Content negotiation lets one URL serve different representations depending on what the client asks for. The client states preferences in request headers, and the server picks the best match.

The core fields are:

  • Accept chooses the media type, such as JSON versus HTML.
  • Accept-Language states preferred languages, such as Accept-Language: de-CH, de;q=0.8, en;q=0.6.
  • Accept-Encoding picks the compression scheme.

The q value, between 0 and 1, expresses relative preference. Higher wins, and omitted means 1. The server answers with matching representation headers, Content-Type, Content-Language, and Content-Encoding, so the client knows what it received.

Negotiation has one cache consequence you must not skip: the Vary header. If a response depends on Accept-Language, send Vary: Accept-Language. Without it, a shared cache can store the German page and serve it to an English reader, since the cache keys on the URL alone.

For localization, treat Accept-Language as a default, not a command. Browsers send the user's configured languages, which may not match what they want on your site. Offer a visible language switcher, store the explicit choice in a cookie or account setting, and let that override the header. Keep the set of negotiated variants small, because each new Vary dimension fragments your cache and lowers the hit rate.

11. How do headers affect performance?

Headers influence speed through compression, caching, and connection handling.

Compression is the biggest single win for text. The client advertises support with Accept-Encoding: gzip, br, and the server replies with Content-Encoding: br after compressing the body. HTML, CSS, JavaScript, and JSON shrink dramatically, while already-compressed formats like JPEG or video gain nothing and should be skipped. Pair compression with Vary: Accept-Encoding so caches keep separate copies.

Caching headers avoid the request altogether. Long max-age values on fingerprinted assets, combined with immutable, mean returning visitors pay no network cost at all. For content that changes, validators (ETag, Last-Modified) turn a full download into a small 304 exchange, as covered earlier.

Connection behavior matters too. HTTP/2 and HTTP/3 multiplex requests over one connection, and header compression shrinks the repeated fields that would otherwise add overhead to every request. Connection headers belong to HTTP/1.x; they are not used in the newer protocols.

Smaller wins add up:

  • Remove unused headers, since every response carries them.
  • Keep cookies off static asset domains.
  • Avoid no-store on content that is safe to cache.

To confirm that your changes landed, inspect real responses instead of trusting config files. A quick pass through the Response Headers Inspector shows whether compression and cache fields actually reach the client.

12. How do headers shape API behavior and versioning?

APIs lean on headers to separate the transport concerns from the resource itself. Authorization carries credentials, Content-Type tells the server how to parse a request body, and Accept tells it what format to return. A mismatched Content-Type can cause a JSON API to reply with an error even though the body looks fine.

Headers also communicate operational state:

  • Retry-After tells clients when to try again after throttling or downtime.
  • ETag with If-Match enables safe concurrent updates: the write succeeds only if the resource has not changed.
  • Location points to a newly created resource.
  • Idempotency keys, sent as a custom field, let clients safely retry a payment or order.

Versioning is a design choice with real trade-offs. You can place the version in the URL, in a custom header, or in a media type such as Accept: application/vnd.example.v2+json. Header-based versioning keeps URLs stable and fits the idea of negotiation, but it is invisible in a browser address bar and easy to forget in tests and docs. If you vary responses by a version header, add it to Vary, or caches may serve the wrong version.

Status codes and headers work together, so when an API misbehaves, read both. Our HTTP Status Codes Reference maps each code to its usual header companions, and our authentication error guide shows how WWW-Authenticate fits in. For programmatic checks, see our Public API.

13. What are custom and non-standard headers, and how should you use them?

A custom header is any field outside the registered standard set. Teams use them for request IDs, feature flags, tenant identifiers, and tracing, for example X-Request-ID: 7f3c9a. They are legitimate and common, as long as you choose names carefully and treat them as part of your interface.

The old convention was to prefix custom names with X-. That practice was later deprecated, because prefixed names that became popular, like X-Forwarded-For, were stuck with the prefix forever. For new fields, choose a clear, descriptive name that is unlikely to collide with a future standard, and check the IANA registry if you intend to share it publicly.

Some rules keep custom headers from causing trouble:

  • Never put secrets in them unless the connection is encrypted and the value is stripped from logs.
  • Do not trust them blindly. Any client can send X-Forwarded-For or a role header, so only honor them when set by infrastructure you control, and strip them at the edge otherwise.
  • Document them like any other API surface, including whether they are required.
  • Mind size and CORS: browsers block cross-origin requests that carry unusual headers unless the server allows them in a preflight response.

Request IDs are the best example of value for little cost. Generate one at the edge, pass it through every service, and log it, and you can trace a single user action across the whole system. When something breaks with a server error, that ID is how you find it; our 500 Internal Server Error reference covers what to do next.

14. Our starting checklist for header hygiene

Most header problems are boring and preventable, so we keep a short list and run it on every project. Set a default Cache-Control so nothing is cached by accident. Add Secure, HttpOnly, and SameSite to every session cookie. Enable HSTS on HTTPS sites once your certificate setup is stable. Deploy CSP in report-only mode first, read the reports, then enforce. Test all of it in staging, and add automated checks so a regression shows up before your users do.

— Andreas

15. Check your headers with tools we built for it

  • SSL Checker to verify the certificate that HSTS and Secure cookies depend on.
  • Website Uptime Monitor to catch availability problems after a header or config change.
  • Traceroute to see where a request slows down on its way to your server.

Pingfloat

FAQ

What is the difference between request and response headers?

Request headers are sent by the client and describe what it wants and who it is, such as Accept, Authorization, and Cookie. Response headers are sent by the server and describe the answer and how to handle it, such as Content-Type, Set-Cookie, and Cache-Control. Representation headers like Content-Length can appear in both directions.

Are HTTP header names case-sensitive?

No. Per RFC 9110, header names are case-insensitive, though some header values are not. HTTP/2 and HTTP/3 send names in lowercase on the wire, so write code that compares names case-insensitively.

Which HTTP headers matter most for security?

Start with Content-Security-Policy, Strict-Transport-Security, and X-Content-Type-Options: nosniff. Add Referrer-Policy and Permissions-Policy for finer control, and use Secure, HttpOnly, and SameSite on cookies. Deploy CSP in report-only mode first so you can find violations before enforcing it.

How can I see the headers a site sends?

Open the Network tab in your browser's DevTools and select a request, or run curl -I against the URL from a terminal. For a quick check with no setup, a web-based header inspector shows the same response headers, including cache and security fields.

Sources