Only one 'header Link' applied

1. Caddy version (caddy version):

v2.4.6 h1:HGkGICFGvyrodcqOOclHKfvJC0qTU7vny/7FhYp9hNw=

2. How I run Caddy:

a. System environment:

Gentoo Linux, OpenRC

b. Command:

caddy run (using Caddyfile)

c. Service/unit/compose file:

d. My complete Caddyfile or JSON config:

wiki.tnonline.net:443 {
	import main wiki.tnonline.net
	@title {
		not file {
			try_files {path} {path}/
			split_path .php
		}
		path_regexp title ^/(.*)$
	}
	@preload {
		path /w/*
	}
	@cache {
		path /mediawiki/resources/assets/* /mediawiki/resources/assets/*/* /mediawiki/images/* /mediawiki/images/*/* /mediawiki/images/*/*/* /favicon.ico /mediawiki/skins/Timeless/resources/images/*
	}
	rewrite @title /mediawiki/index.php?title={re.title.1}&{query}
	redir / /w/Main_Page

    header {
		Permissions-Policy interest-cohort=()
		Strict-Transport-Security "max-age=15768000; preload"
		X-Frame-Options DENY
		#X-Content-Type-Options nosniff
	}
	header @cache {
		Cache-Control max-age=31536000
	}
	header @preload {
		Link "</mediawiki/resources/assets/fonts/KFOmCnqEu92Fr1Mu4mxK.woff2>; rel=preload; as=font"
		Link "</mediawiki/resources/assets/fonts/KFOmCnqEu92Fr1Mu4mxK.woff2-test>; rel=preload; as=font"
#		Link "</mediawiki/load.php?lang=en-gb&modules=site.styles&only=styles&skin=timeless>; rel=preload; as=style"
#		Link "</mediawiki/load.php?lang=en-gb&modules=ext.visualEditor.desktopArticleTarget.noscript%7Cskins.timeless&only=styles&skin=timeless>; rel=preload; as=style"
	}
	root * /var/www/domains/wiki.tnonline.net/htdocs
	php_fastcgi unix//var/run/php-fpm/fpm-wiki.socket
	file_server
}

3. The problem Iā€™m having:

The problem I have is that only the last Link header is added. In this case the KFOmCnqEu92Fr1Mu4mxK.woff2-test. I do want to add the two stylesheets as well but the result is the same.

# curl -I --http2 https://wiki.tnonline.net/w/Main_Page
HTTP/2 200
alt-svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000
cache-control: no-cache, no-store, max-age=0, must-revalidate
content-language: en-GB
content-type: text/html; charset=UTF-8
expires: Thu, 01 Jan 1970 00:00:00 GMT
link: </mediawiki/resources/assets/fonts/KFOmCnqEu92Fr1Mu4mxK.woff2-test>; rel=preload; as=font
permissions-policy: interest-cohort=()
pragma: no-cache
server: Caddy
strict-transport-security: max-age=15768000; preload
vary: Accept-Encoding, Cookie
x-content-type-options: nosniff
x-frame-options: DENY
x-request-id: 99fdea95d1e529bd78406321
date: Sat, 01 Jan 2022 09:17:43 GMT

4. Error messages and/or full log output:

{
	"level": "info",
	"ts": 1641028663.0280214,
	"logger": "http.log.access.log21",
	"msg": "handled request",
	"request": {
		"remote_addr": "[2001:470:28:704::100]:37086",
		"proto": "HTTP/2.0",
		"method": "HEAD",
		"host": "wiki.tnonline.net",
		"uri": "/w/Main_Page",
		"headers": {
			"User-Agent": [
				"curl/7.79.1"
			],
			"Accept": [
				"*/*"
			]
		},
		"tls": {
			"resumed": false,
			"version": 772,
			"cipher_suite": 4865,
			"proto": "h2",
			"proto_mutual": true,
			"server_name": "wiki.tnonline.net"
		}
	},
	"common_log": "2001:470:28:704::100 - - [01/Jan/2022:10:17:43 +0100] \"HEAD /w/Main_Page HTTP/2.0\" 200 0",
	"user_id": "",
	"duration": 0.070666508,
	"size": 0,
	"status": 200,
	"resp_headers": {
		"Expires": [
			"Thu, 01 Jan 1970 00:00:00 GMT"
		],
		"Cache-Control": [
			"no-cache, no-store, max-age=0, must-revalidate"
		],
		"Pragma": [
			"no-cache"
		],
		"X-Request-Id": [
			"99fdea95d1e529bd78406321"
		],
		"Alt-Svc": [
			"h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000"
		],
		"Strict-Transport-Security": [
			"max-age=15768000; preload"
		],
		"Content-Language": [
			"en-GB"
		],
		"Vary": [
			"Accept-Encoding, Cookie"
		],
		"X-Content-Type-Options": [
			"nosniff"
		],
		"Content-Type": [
			"text/html; charset=UTF-8"
		],
		"Link": [
			"</mediawiki/resources/assets/fonts/KFOmCnqEu92Fr1Mu4mxK.woff2-test>; rel=preload; as=font"
		],
		"X-Frame-Options": [
			"DENY"
		],
		"Permissions-Policy": [
			"interest-cohort=()"
		],
		"Server": [
			"Caddy"
		]
	}
}

5. What I already tried:

I tried to nest like so:

header {
   header @cache {
      }
}

Could route be an option to use?

6. Other info

Ultimately Iā€™d like to use HTTP push for some resources if the user doesnā€™t have the resource cached, but how can I tell if a user havenā€™t visited the site before? I am thinking I could set a cookie, and then check for it. If not set, then use HTTP push, otherwise just add the preload link header.

I think you need to use the + prefix to make sure the header is added instead of replaced. For example:

	header @preload {
		+Link "</mediawiki/resources/assets/fonts/KFOmCnqEu92Fr1Mu4mxK.woff2>; rel=preload; as=font"
		+Link "</mediawiki/resources/assets/fonts/KFOmCnqEu92Fr1Mu4mxK.woff2-test>; rel=preload; as=font"
	}

Make sure to also enable the push directive (I donā€™t see it in your config, I suppose it might be part of the imported snippet though, please make sure to post your entire config next time):

Hi. Sorry I forgot to add the imported parts. Thatā€™s to setup logging and TLS which is common to several vhosts.
This is the main Caddyfile.


	# debug
	auto_https off
	log {
		output file /var/log/caddy/main/caddy_main.log {
			roll_size 100MiB
			roll_keep_for 100d
		}
		format json
	}
	servers :443 {
		protocol {
			experimental_http3
		}
	}
	servers :80 {
		protocol {
			allow_h2c
		}
	}
}

## Snippets

(main) {
	tls /etc/letsencrypt/live/{args.0}/fullchain.pem /etc/letsencrypt/live/{args.0}/privkey.pem {
		curves x25519 secp384r1 secp256r1
	}
	log {
		output file /var/log/caddy/{args.0}_443.log {
			roll_size 100MiB
			roll_keep_for 100d
		}
		format json
	}
	encode zstd gzip
}

## Hosts section

import vhosts/*.caddy

I think youā€™re right that using +Link should work. Unfortunately adding +Link didnā€™t help and only the last item gets added to the output.

Currently I donā€™t want to enable push, as I donā€™t yet have a method to determine if it is likely that a client has the resources cached or not. Link preload should, however give browsers a chance to load it from cache, or request it early if needed.

Edit: with push enabled, caddy pushed the one Link only, just like above.

I verified this with nghttp -ns https://wiki.tnonline.net/w/Main_Page .

Alright, turns out you need to do it like this:

	header @preload +Link "</mediawiki/resources/assets/fonts/KFOmCnqEu92Fr1Mu4mxK.woff2>; rel=preload; as=font"
	header @preload +Link "</mediawiki/resources/assets/fonts/KFOmCnqEu92Fr1Mu4mxK.woff2-test>; rel=preload; as=font"

A separate header directive for each header you want to add, and + is necessary.

Iā€™ll need to do a bit more digging to figure out if thereā€™s actually support in the header handler (in JSON config) for setting more than one value for a header at the same timeā€¦ it might be a Caddyfile parsing oversight :thinking:

2 Likes

Thanks. It works as expected now, both with and without push enabled.

Should I open an issue on github about this?

Happy New year :blush:

Sure, that would help remind me :+1:

You too! Happy New Year :tada:

2 Likes

I wrote the PR to fix it. Super simple.

2 Likes

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