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.