Cannot modify/remove a header after importing it as a snippet

1. Caddy version (caddy version):

v2.4.5

2. How I run Caddy:

a. System environment:

Ubuntu 20.04.3 LTS via systemd

b. Command:

n/a

c. Service/unit/compose file:

n/a

d. My complete Caddyfile or JSON config:

(sec_headers) {
	header {
		# enable HSTS
		Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
		X-Frame-Options "deny"

		# Default "Caddy"
		Server "test_server"
	}
}

sub1.yourdomain.com {
	import sec_headers

	#
	# on this subdomain I want to remove <X-Frame-Options> header completely 
	# while keeping the rest improted from (sec_headers snippet) headers
	# PROBLEM: X-FRAME-OPTIONS is not removed
	#

	header -X-Frame-Options
	encode gzip
	reverse_proxy 127.0.0.1:1122
}


sub2.yourdomain.com {
	import sec_headers

	#
	# on this subdomain I want to replace the value <X-Frame-Options> 
	# from "deny" --> "SAMEORIGIN" and the rest imported from (sec_headers) headers
	# PROBLEM: X-FRAME-OPTIONS still shows "deny" as its value
	# tried ?X-Frame-Options "SAMEORIGIN" --> same result

	header X-Frame-Options "SAMEORIGIN"
	encode gzip
	reverse_proxy 127.0.0.1:2233
}


3. The problem I’m having:

Cannot overwrite or remove any specific header which was previously imported via a snippet. (see both scenarios above for sub1. and sub2. subdomains)

I’d like avoid writing the same headers for each sub-domain and remove / modify only specific ones after importing the whole batch. I am not sure how to overwrite or remove the header after you imported it via a snippet.

4. Error messages and/or full log output:

5. What I already tried:

If I comment import sec_headers the value will change from denySAMEORIGIN otherwise it will remain deny

sub3.yourdomain.com {
	#import sec_headers
	encode gzip
	reverse_proxy 127.0.0.1:2233 {
			header_down X-Frame-Options SAMEORIGIN
	}
}

6. Links to relevant resources:

similar issue:

Just don’t include that header in the snippet if it doesn’t actually apply to all sites.

Or, consider using import arguments to specify the value to replace in the snippet.

well, it’s an option, but it’s going the .htaccess way again, when we basically creating a set of headers for each subdomain in separate files and importing them… I was hoping to find a more efficient solution, which uses DRY principle.

Majority of the headers are being reused across multiple subdomains, and we only change some values for certain headers (overriding previous from import) or sometimes remove the header completely (e.g. header -Server).

Wouldn’t it make more sense to prioritize parsing? Say,

  • if import is the only method used → use the headers defined in import
  • if import is used AND user decides to override some values or exclude a header manually either via header_request, header_up, header_down, header → override import and use modified values?

The Caddyfile adapter’s job is to take a Caddyfile as input, and output a JSON config. Run caddy adapt --pretty to see what happens.

The “priority” at runtime is the order in which handlers appear in the JSON config. Adding a separate parallel “priority” to this would add undue complexity that would make Caddy harder to use correctly.

Technically, headers can be specified more than once in a response. This is something Caddy tries to allow, to be spec compliant.

To be clear, importing snippets in the Caddyfile is just fancy copy-paste. It’s as simple as that. The JSON config has no concept of import because it’s just syntax sugar provided by the Caddyfile. So it’s not possible to have runtime logic surrounding import.

Like I said earlier, using arguments to import is probably the best way to avoid repetition for you in this case. Use {args.0} as a placeholder for the header value, and pass that value as the first argument to the snippet you’re importing.

2 Likes

Right, thanks for explaining how the parsing process works. It’s more clear now why it was designed this way

I think I’ll go with a hybrid mode : defining a new set of headers for specific domain and using args for in other snippets which can be reused.

1 Like