Via header (tested)
Via is the header proxies use to say "this message passed through me". Each intermediary appends one entry: the protocol version it received and a name for itself. You mostly see it on responses that came through a CDN or cache. We fetched eight big sites, checked whether clients add it (they do not), and built a tiny proxy to show how entries chain and how loops are caught.
1. Which big sites return Via (measured)
One curl -sI per site on 2026-10-02, curl 8.14.1, HTTP/2, from a London connection:
| Site | Via in response | Other clues |
|---|---|---|
| aws.amazon.com | 1.1 c481ec027cb328d503dd63ec0db0d2ca.cloudfront.net (CloudFront) | x-cache: Miss from cloudfront, x-amz-cf-pop: LHR3-P3 |
| d1.awsstatic.com | 1.1 735ca864d71c76476e2ce475a166303c.cloudfront.net (CloudFront) | server: AmazonS3, x-cache: Hit from cloudfront |
| www.reddit.com | 1.1 varnish | server: snooserv |
| www.cloudflare.com | none | server: cloudflare, cf-ray: ...-LHR |
| www.fastly.com | none (also none over HTTP/1.1) | x-served-by: cache-sjc10035-SJC, cache-lhr-...-LHR, x-cache: HIT, HIT |
| www.wikipedia.org | none | server: ATS/9.2.15, x-cache: cp3069 miss, cp3069 hit/599418 |
| www.google.com | none | server: gws |
| github.com | none | server: github.com |
Three things stood out:
- CloudFront put a different hostname in
Viaon a second request to aws.amazon.com (8a8ae069...cloudfront.net). The hex part names the edge server, so do not match on it; match on(CloudFront)orcloudfront.net. - Fastly, Cloudflare and Wikipedia's caches clearly sit in front of those sites, but none sent
Via. NoViadoes not mean no proxy. Look atx-cache,x-served-by,cf-rayandserveras well. Vialeaks a little about your stack (varnish,CloudFront). That is usually harmless, but it is the same trade-off as the Server header.
curl -sI https://aws.amazon.com/ | grep -i '^via'
via: 1.1 c481ec027cb328d503dd63ec0db0d2ca.cloudfront.net (CloudFront)
2. Syntax (from RFC 9110, not measured)
RFC 9110 section 7.6.3 defines each entry as received-protocol received-by [comment], with entries separated by commas:
Via: 1.0 fred, 1.1 p.example.net (Apache/1.1)
^ ^ ^ ^ ^
| | | | optional comment
| | | received-by: host[:port] or a pseudonym
| | received-protocol (name "HTTP/" is omitted)
| first proxy
version that proxy received
- received-protocol is the version the proxy received, with
HTTP/dropped:1.1,2,3. Other protocols keep their name, e.g.HTTP/2is written2but a non-HTTP one would beFOO/1.0. - received-by may be a real host and port or a pseudonym like
varnish. The RFC lets a proxy hide real hostnames. - Each proxy appends, so the leftmost entry is the first hop. A proxy must add Via on both requests it forwards and responses it returns.
3. Do clients add Via? (measured: no)
A Node.js 24.21.0 echo server on 127.0.0.1:8985 returned the headers it received:
| Client | Via sent | Headers seen by server |
|---|---|---|
| curl 8.14.1 | no | host, user-agent, accept |
Node.js 24 fetch() | no | host, connection, accept, accept-language, sec-fetch-mode, user-agent, accept-encoding |
So if your origin logs show a Via request header, something in between (a CDN, a corporate proxy, a load balancer) added it.
4. How entries chain and how loops are caught (measured on our own proxy)
We wrote a 15-line Node proxy on 127.0.0.1:8986 that appends 1.1 test-proxy to Via on the request and the response, and refuses a request that already contains its own name:
const via = (req.headers.via ? req.headers.via + ', ' : '') + '1.1 test-proxy';
if ((req.headers.via || '').includes('test-proxy')) {
res.statusCode = 508; // Loop Detected
return res.end('loop detected\n');
}
| Request sent to the proxy | Via the origin received | Result |
|---|---|---|
| No Via | 1.1 test-proxy | 200, response also had via: 1.1 test-proxy |
Via: 1.1 cdn-edge | 1.1 cdn-edge, 1.1 test-proxy | 200 |
Via: 1.1 test-proxy | (not forwarded) | HTTP/1.1 508 Loop Detected |
That is the whole idea of Via-based loop detection: if a proxy sees its own name already in the list, the request has come back around. The 508 status is our choice for the test, not a standard proxy rule. From the docs, not measured: CDNs increasingly use the separate CDN-Loop header (RFC 8586) for the same purpose, because it survives customers stripping Via; Cloudflare documents adding CDN-Loop: cloudflare to requests it sends to your origin.
5. Hiding or adding Via (from the docs, not measured)
| Server | Directive | Effect |
|---|---|---|
| nginx (as reverse proxy) | proxy_hide_header Via; | Drops the upstream's Via from responses to clients. nginx does not add Via itself. |
| nginx | proxy_set_header Via ""; | Stops a client's Via from reaching the upstream (empty value = header not sent). |
| Apache mod_proxy | ProxyVia Off | On | Full | Block | Default Off passes Via through untouched; On/Full add an entry; Block removes it. |
| Varnish | unset resp.http.Via; in vcl_deliver | Removes the 1.1 varnish entry Varnish adds. |
location / {
proxy_pass http://backend;
proxy_hide_header Via;
}
Removing Via from responses is cosmetic. Do not remove it between your own proxies if any of them rely on it for loop detection.
6. Via vs Forwarded / X-Forwarded-For
Via records the proxies a message went through. It says nothing about the client. To find the original client IP, use Forwarded or X-Forwarded-For; see Forwarded vs X-Forwarded-For. In our proxy test the echo server saw Via but no X-Forwarded-For, because our proxy, like nginx by default, does not add one unless told to.
Frequently asked questions
What does the Via header mean?
It lists the proxies a request or response passed through. Each entry is the protocol version the proxy received plus a name for the proxy, e.g. <code>1.1 varnish</code> or <code>1.1 abc123.cloudfront.net (CloudFront)</code>.
Does my browser or curl send Via?
No. In our test curl 8.14.1 and Node.js 24 fetch sent no Via header. If your server sees one, a proxy or CDN between you added it.
Which CDNs add Via?
On 2026-10-02 CloudFront (aws.amazon.com, d1.awsstatic.com) and Varnish (reddit.com) returned Via. Cloudflare, Fastly, Google, GitHub and Wikipedia did not, even though most of them run caches. Missing Via does not mean there is no proxy.
How does Via prevent proxy loops?
Each proxy appends its own name. If a proxy receives a request whose Via already contains its name, the request has looped and the proxy can refuse it. Our test proxy did this and returned 508 Loop Detected. Many CDNs now use the CDN-Loop header for this (from the docs, not measured).
How do I remove the Via header in nginx?
From the nginx docs, not measured: add <code>proxy_hide_header Via;</code> in the proxied location to drop the upstream's Via from responses. nginx does not add Via on its own.
Is Via the same as X-Forwarded-For?
No. Via lists the proxies; X-Forwarded-For and Forwarded carry the original client IP. They are often used together.
Written with AI assistance. Measured on 2026-10-02 with curl 8.14.1 (one request each to www.cloudflare.com, www.fastly.com, aws.amazon.com, d1.awsstatic.com, www.google.com, github.com, www.wikipedia.org and www.reddit.com, over HTTP/2 from London) and Node.js 24.21.0 (an echo server on localhost:8985 and a 15-line test proxy on localhost:8986, plus built-in fetch). Via syntax is quoted from RFC 9110; nginx, Apache and CDN behaviour we did not reproduce is labelled as from the docs, not measured. PlainHeaders is our extension; curl and Node.js are free.