About setting headers in header block

1. The problem I’m having:

I have some default headers set in a header block.
At this time, if the header is overwritten when connecting with reverse_proxy or php_fastcgi, other headers may disappear.
I am not sure if this is a bug or specification, so I asked this question.

2. Error messages and/or full log output:

Curl result

curl -Li localhost:8080/test1

HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Custom-Header1: 1
Custom-Header2: 2
Custom-Header3: 3
Server: Caddy
Date: Mon, 25 Nov 2024 11:06:15 GMT
Content-Length: 4

Hey!

→OK

curl -Li localhost:8081

HTTP/1.1 200 OK
Content-Length: 6
Content-Type: text/plain; charset=utf-8
Custom-Header1: 4
Date: Mon, 25 Nov 2024 11:07:00 GMT
Server: Caddy
Server: Caddy

Hello!

→Custom-Header2 and Custom-Header3 do not exist.

Caddy log

2024/11/25 11:06:10.806 INFO    using config from file  {"file": "./Caddyfile"}
2024/11/25 11:06:10.808 INFO    adapted config to JSON  {"adapter": "caddyfile"}
2024/11/25 11:06:10.824 INFO    admin   admin endpoint started  {"address": "localhost:2019", "enforce_origin": false, "origins": ["//localhost:2019", "//[::1]:2019", "//127.0.0.1:2019"]}
2024/11/25 11:06:10.824 INFO    http.auto_https automatic HTTPS is completely disabled for server       {"server_name": "srv0"}
2024/11/25 11:06:10.824 INFO    tls.cache.maintenance   started background certificate maintenance      {"cache": "0xc000532380"}
2024/11/25 11:06:10.824 INFO    http.auto_https automatic HTTPS is completely disabled for server       {"server_name": "srv1"}
2024/11/25 11:06:10.824 INFO    http.auto_https automatic HTTPS is completely disabled for server       {"server_name": "srv2"}
2024/11/25 11:06:10.825 DEBUG   http.auto_https adjusted config {"tls": {"automation":{"policies":[{}]}}, "http": {"servers":{"srv0":{"listen":[":8080"],"routes":[{"handle":[{"handler":"subroute","routes":[{"handle":[{"handler":"headers","response":{"require":{"headers":{"Custom-Header1":null,"Custom-Header2":null,"Custom-Header3":null}},"set":{"Custom-Header1":["1"],"Custom-Header2":["2"],"Custom-Header3":["3"]}}},{"body":"Hey!","handler":"static_response"}]}]}],"terminal":true}],"automatic_https":{"disable":true,"skip":["localhost"]}},"srv1":{"listen":[":8081"],"routes":[{"handle":[{"handler":"subroute","routes":[{"handle":[{"handler":"headers","response":{"require":{"headers":{"Custom-Header1":null,"Custom-Header2":null,"Custom-Header3":null}},"set":{"Custom-Header1":["1"],"Custom-Header2":["2"],"Custom-Header3":["3"]}}},{"handler":"reverse_proxy","upstreams":[{"dial":"localhost:9000"}]}]}]}],"terminal":true}],"automatic_https":{"disable":true,"skip":["localhost"]}},"srv2":{"listen":[":9000"],"routes":[{"handle":[{"handler":"subroute","routes":[{"handle":[{"handler":"headers","response":{"set":{"Custom-Header1":["4"]}}},{"body":"Hello!","handler":"static_response"}]}]}],"terminal":true}],"automatic_https":{"disable":true,"skip":["localhost"]}}}}}
2024/11/25 11:06:10.826 DEBUG   http    starting server loop    {"address": "[::]:8080", "tls": false, "http3": false}
2024/11/25 11:06:10.826 INFO    http.log        server running  {"name": "srv0", "protocols": ["h1", "h2", "h3"]}
2024/11/25 11:06:10.828 DEBUG   http    starting server loop    {"address": "[::]:8081", "tls": false, "http3": false}
2024/11/25 11:06:10.828 INFO    http.log        server running  {"name": "srv1", "protocols": ["h1", "h2", "h3"]}
2024/11/25 11:06:10.828 DEBUG   http    starting server loop    {"address": "[::]:9000", "tls": false, "http3": false}
2024/11/25 11:06:10.829 INFO    http.log        server running  {"name": "srv2", "protocols": ["h1", "h2", "h3"]}
2024/11/25 11:06:10.831 INFO    autosaved config (load with --resume flag)      {"file": "C:\\Users\\riku\\.config\\caddy\\autosave.json"}
2024/11/25 11:06:10.831 INFO    serving initial configuration
2024/11/25 11:06:10.835 INFO    tls     storage cleaning happened too recently; skipping for now        {"storage": "FileStorage:C:\\Users\\riku\\AppData\\Roaming\\Caddy", "instance": "0b5f1bdc-2683-472c-ad1d-b34106f76c5c", "try_again": "2024/11/26 11:06:10.835", "try_again_in": 86400}
2024/11/25 11:06:10.836 INFO    tls     finished cleaning storage units
2024/11/25 11:07:00.129 DEBUG   http.handlers.reverse_proxy     selected upstream       {"dial": "localhost:9000", "total_upstreams": 1}
2024/11/25 11:07:00.143 DEBUG   http.handlers.reverse_proxy     upstream roundtrip      {"upstream": "localhost:9000", "duration": 0.0136276, "request": {"remote_ip": "::1", "remote_port": "3836", "client_ip": "::1", "proto": "HTTP/1.1", "method": "GET", "host": "localhost:8081", "uri": "/", "headers": {"Accept": ["*/*"], "X-Forwarded-For": ["::1"], "X-Forwarded-Proto": ["http"], "X-Forwarded-Host": ["localhost:8081"], "User-Agent": ["curl/8.11.0"]}}, "headers": {"Content-Type": ["text/plain; charset=utf-8"], "Custom-Header1": ["4"], "Server": ["Caddy"], "Date": ["Mon, 25 Nov 2024 11:07:00 GMT"], "Content-Length": ["6"]}, "status": 200}

3. Caddy version:

v2.8.4 h1:q3pe0wpBj1OcHFZ3n/1nl4V4bxBrYoSoab7rL9BMYNk=

4. How I installed and ran Caddy:

scoop install caddy

a. System environment:

Windows11 24H2 64bit

b. Command:

cd E:\caddyfile_test
caddy run --config ./Caddyfile

d. My complete Caddy config:

{
	auto_https off
	debug
}

(default_headers) {
	header {
		?Custom-Header1 "1"
		?Custom-Header2 "2"
		?Custom-Header3 "3"
	}
}

http://localhost:8080 {
	import default_headers
	respond "Hey!"
}

http://localhost:8081 {
	import default_headers
	reverse_proxy localhost:9000
}

http://localhost:9000 {
	header Custom-Header1 "4"
	respond "Hello!"
}

P.S

I noticed this while testing in another environment at hand (Ubuntu 24.04.1, installed from the official apt repository and running with Systemd).
The actual environment uses php_fastcgi, but for simplicity this time I created a test Caddyfile with reverse_proxy.
I have also confirmed that setting default_headers as follows solves the problem.

(default_headers) {
	header ?Custom-Header1 "1"
	header ?Custom-Header2 "2"
	header ?Custom-Header3 "3"
}

Yeah there’s a note about that in the docs header (Caddyfile directive) — Caddy Documentation for how ? works.

Basically if you group it up all in one header directive, it applies all of those as one condition, not 3 separate conditions. So it only applies if all three of those are empty, not just when each are empty.

So yes, splitting it up into separate header directives is the correct fix.

1 Like

Hello.

Thank you for your reply.

Ah, I read the documentation, but I couldn’t quite understand it and I overlooked it.
I am very sorry.
It might be a good idea to include not only an explanation, but also an example that doesn’t work like this one.
Thank you very much.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.