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:

SiteVia in responseOther clues
aws.amazon.com1.1 c481ec027cb328d503dd63ec0db0d2ca.cloudfront.net (CloudFront)x-cache: Miss from cloudfront, x-amz-cf-pop: LHR3-P3
d1.awsstatic.com1.1 735ca864d71c76476e2ce475a166303c.cloudfront.net (CloudFront)server: AmazonS3, x-cache: Hit from cloudfront
www.reddit.com1.1 varnishserver: snooserv
www.cloudflare.comnoneserver: cloudflare, cf-ray: ...-LHR
www.fastly.comnone (also none over HTTP/1.1)x-served-by: cache-sjc10035-SJC, cache-lhr-...-LHR, x-cache: HIT, HIT
www.wikipedia.orgnoneserver: ATS/9.2.15, x-cache: cp3069 miss, cp3069 hit/599418
www.google.comnoneserver: gws
github.comnoneserver: github.com

Three things stood out:

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

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:

ClientVia sentHeaders seen by server
curl 8.14.1nohost, user-agent, accept
Node.js 24 fetch()nohost, 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 proxyVia the origin receivedResult
No Via1.1 test-proxy200, response also had via: 1.1 test-proxy
Via: 1.1 cdn-edge1.1 cdn-edge, 1.1 test-proxy200
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)

ServerDirectiveEffect
nginx (as reverse proxy)proxy_hide_header Via;Drops the upstream's Via from responses to clients. nginx does not add Via itself.
nginxproxy_set_header Via "";Stops a client's Via from reaching the upstream (empty value = header not sent).
Apache mod_proxyProxyVia Off | On | Full | BlockDefault Off passes Via through untouched; On/Full add an entry; Block removes it.
Varnishunset resp.http.Via; in vcl_deliverRemoves 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.