Via x.y headers

1. The problem I’m having:

Caddy is generating Via x.y Caddy (x.y being for example 0.0, 1.1, etc) headers for certain requests and I cannot figure out why. Does it have to do with how certain blocks/directives generate their own “server” ?

2. Error messages and/or full log output:

$ curl -I https://myhost.nl/apple-touch-icon.png
HTTP/2 200
accept-ranges: bytes
access-control-allow-headers: *
access-control-allow-methods: OPTIONS, GET, POST, PUT, PATCH, DELETE
access-control-allow-origin: *
access-control-expose-headers: *
alt-svc: h3=":443"; ma=2592000
cache-control: public, max-age=604800, s-maxage=86400, stale-while-revalidate=3600, must-revalidate
content-type: image/png
etag: "chpq5h51srdu4i3"
last-modified: Mon, 07 Feb 2022 09:58:36 GMT
referrer-policy: same-origin, strict-origin-when-cross-origin
vary: Accept-Encoding
via: Caddy Development Proxy
x-content-type-options: nosniff
x-frame-options: DENY
content-length: 5835
date: Fri, 06 Jun 2025 12:42:27 GMT

$ curl -I https://myhost.nl/
HTTP/2 200
access-control-allow-headers: *
access-control-allow-methods: OPTIONS, GET, POST, PUT, PATCH, DELETE
access-control-allow-origin: *
access-control-expose-headers: *
alt-svc: h3=":443"; ma=2592000
cache-control: max-age=3600, public, s-maxage=1800
content-type: text/html; charset=UTF-8
date: Fri, 06 Jun 2025 12:42:33 GMT
referrer-policy: same-origin, strict-origin-when-cross-origin
set-cookie: customers_deauth_profile_token=efb74d; path=/; httponly; samesite=lax
set-cookie: customers_auth_profile_token=deleted; expires=Thu, 06 Jun 2024 12:42:33 GMT; Max-Age=0; path=/; httponly
set-cookie: _session_staging=hu3PfEMtE9e5R-hPZCeDi%2Ct3B5GnArG-Z8TRCB0WlfXK2dToGSjXxsL5VtmBheDx; expires=Sun, 08 Jun 2025 12:42:34 GMT; Max-Age=172800; path=/; secure; httponly; samesite=lax
via: Caddy Development Proxy
via: 0.0 Caddy
x-content-type-options: nosniff
x-frame-options: DENY

3. Caddy version:

v2.10.0 h1:fonubSaQKF1YANl8TXqGcn4IbIRUDdfAkpcsfI/vX5U=

4. How I installed and ran Caddy:

a. System environment:

Ubuntu;

apt install caddy

b. Command:

systemctl start caddy

c. Service/unit/compose file:

d. My complete Caddy config:

myhost.nl {

  root * /srv/myhost.nl/web

  encode zstd gzip

  header -server
  header +via "Caddy Development Proxy"

  # CORS header defaults (allows backend headers to take precedence)
  # https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
  header ?access-control-allow-origin *
  header ?access-control-allow-methods "OPTIONS, GET, POST, PUT, PATCH, DELETE"
  header ?access-control-allow-headers *
  header ?access-control-expose-headers *

  # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options
  header ?x-content-type-options nosniff

  # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
  header ?x-frame-options DENY

  # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy
  header ?referrer-policy "same-origin, strict-origin-when-cross-origin"

  @exists file
  @static path *.jpg *.jpeg *.js *.css *.png *.woff *.woff2 *.otf *.ttf *.gif *.svg *.webp

  handle @exists {
    header @static ?cache-control "public, max-age=604800, s-maxage=86400, stale-while-revalidate=3600, must-revalidate"

    file_server
  }

  @noproxy path /actievoorwaarden/* /algemenevoorwaarden/* /amazon/* /assets/* /build/* /images/* /img/*

  handle @noproxy {
    try_files {path} =404
  }

  handle {
    php_fastcgi unix//run/php/php-fpm.sock {
      resolve_root_symlink

      env APP_ENV uat

      try_files /app.php{path}
    }
  }

  log {
    output file /var/log/caddy/host.log {
      roll_disabled # use logrotate.d
    }
    format json
  }
}

The Via header is added where reverse_proxy is used. This is expected and correct behavior based on the lengthy discussion here:

That is not my question though. I am wondering where this numbering is coming from or what it is based on, and whether or not I can influence the value produced. I am already adding my own Via headers so I know which “proxies” the requests pass through. But now I am also seeing these headers that tell me nothing useful about their origin.

The number is the protocol version used in the proxy connection

Ok.. so protocol is unknown. And version is 0.0 or 1.1. That’s.. peculiar, and not very helpful. I guess it is because php_fastcgi is a type of upstream/reverse proxy?

That’d be my guess, but I haven’t traced the code to confirm.

If I’m reading the code correctly, Caddy adds one Via header in each direction - one for the request going to the backend, and one for the response coming back to the client. So the backend sees one Via header, and the client sees another.

On the way from the client to the backend, Caddy adds a Via header based on the client-side request protocol version:

On the way back from the backend to the client, Caddy adds another Via header, this time based on the backend-side protocol version:

I believe that’s the one you’re interested in.

Now, when you’re using a FastCGI socket (or FastCGI in general) instead of an HTTP upstream, the response isn’t a full HTTP response in the traditional sense. It doesn’t include a defined HTTP version. As a result, Caddy ends up with default values of 0 for both the major and minor protocol versions.

Now that I think about it, people should probably remove or restrict who can see the Via response header. It’s not necessarily a big deal, but it does leak some information about the backend’s capabilities.

In other words, treat the response Via header the same way you’d treat the Server header.

In other words, leave it alone. :wink:

Security through obscurity isn’t security, it’s a distraction.