Removing only one of the Server headers

1. The problem I’m having:

I am using Caddy to terminate SSL, with reverse_proxy. Caddy adds a Server header to the response. But the response from the upstream already has a Server header. Which means I now have two Server headers. I would like to remove just the Caddy Server header.

2. Error messages and/or full log output:

I looks like I can only remove both Server headers at once. Am I missing something?
Or is is perhaps possible to join the two headers so I can use a replace to clean up?

3. Caddy version:

$ caddy version
2.6.2

4. How I installed and ran Caddy:

$ sudo apt-get install caddy

a. System environment:

Ubuntu 24.04 server

b. Command:

$ sudo systemctl start caddy

c. Service/unit/compose file:


d. My complete Caddy config:

{
        http_port 6080
        https_port 6443
        auto_https disable_redirects
        grace_period 7s
        email webmaster@okaphone.nl
}

okaphone.com:6080,
www.okaphone.com:6080,
kassa.okaphone.com:6080,
ntp.okaphone.com:6080,
webcam.okaphone.com:6080,
stats.okaphone.com:6080,
okaphone.com:6443,
www.okaphone.com:6443,
kassa.okaphone.com:6443,
ntp.okaphone.com:6443,
webcam.okaphone.com:6443,
stats.okaphone.com:6443 {
        reverse_proxy proxy.okaphone.com:8080 {
                header_up Host {hostport}
        }       # Varnish
        header -Server
}

svn.okaphone.com:6080 {
        respond Forbidden 403 {
                close
        }
}

svn.okaphone.com:6443 {
        reverse_proxy svn.okaphone.com:7080 {
                header_up Host {hostport}
        }       # Apache
}

# Refer to the Caddy docs for more information:
# https://caddyserver.com/docs/caddyfile

5. Links to relevant resources:

Put header_down -Server into the reverse_proxy block you want to remove the Server header from, as per the documentation.

stats.okaphone.com:6443 {
        reverse_proxy proxy.okaphone.com:8080 {
                header_up Host {hostport}
                header_down -Server
        }       # Varnish
}

Also, consider upgrading Caddy using the official repository.

That removes the wrong header. I would lose the information I need. :frowning:

Maybe Caddy should use a Via header when it is reverse proxying. But as it is, I would be happy if I could just remove the Caddy Server header.

We do, but Debian hasn’t upgraded Caddy in ages. You’ll get that behavior once you use our official repository and upgrade Caddy.

1 Like

Ah, good. Well, if you advise against using Caddy on Ubuntu because of that, I should perhaps take a look at Nginx to see if that will do the SSL-termination job for me.

It’s not a hobby… What I really need is LTS, much more so than the absolute latest and greatest version of all of the software I’m using. :wink:

1 Like

Maybe I should consider using X-Server in the upstreams. Then I can remove the Server header and will be left with what I need. It’s not pretty but it’s a possibility.

That is if there is no way to remove just the Caddy Server header. Nobody said it yet, but I gather that is the answer: Can’t.

Nobody advised this. We’re telling you use Caddy’s official repository, as listed on the Caddy official documentation, which is linked by @techjedialex. The package provided by Debian/Ubuntu is not always up to date. Ours (official Caddy repository) is always up to date.

That’s false. As I already said, upgrade Caddy to latest and you’ll see it using the Via header while keeping the Server header from upstream.

1 Like

And another idea:

Is there a placeholder that will give me de value of the Server header from the upstream response? All I could find was {http.response.header.Server"} but that is not the one from the upstream. It just gives me “Caddy”.

I think I could do something if such a placeholder exists.

Can’t guarantee this will work with your old v2.6.2, but give it a try:

example.com {
  tls internal

  reverse_proxy localhost:8080 {
    handle_response {
      header X-Server {rp.header.Server}
      copy_response
    }
  }
  header -Server
}

:8080 {
  header Server "Upstream WebServer"
  respond "upstream" 200
}

Test:

$ curl https://example.com -i
HTTP/2 200 
alt-svc: h3=":443"; ma=2592000
content-type: text/plain; charset=utf-8
date: Wed, 25 Mar 2026 01:12:54 GMT
via: 1.1 Caddy
x-server: Upstream WebServer
content-length: 8

upstream
2 Likes

Also, not sure again how that behaved in v2.6.2, but if you want to hardcode a specific Server header, you can just do this:

example.com {
  tls internal

  reverse_proxy localhost:8080 {
    header_down Server "My Webserver"
  }
}

Test:

$ curl https://example.com -i
HTTP/2 200 
alt-svc: h3=":443"; ma=2592000
content-type: text/plain; charset=utf-8
date: Wed, 25 Mar 2026 01:24:53 GMT
server: My Webserver
via: 1.1 Caddy
content-length: 8

upstream
1 Like
... {
        reverse_proxy proxy.okaphone.com:8080 {
                header_up Host {hostport}
                header_down -Server
                handle_response {
                        header Server "^Caddy$" "{rp.header.Server}"
                        copy_response
                }
        }       # Varnish
        header Via "Caddy"
}

This works! :star_struck:

There, added the Via for good measure.