iOS/Android well-known files

Hello everyone,

I’m using Caddy to host a couple Angular2 applications, so far, without any issues or, at least, major issues.

However, I’m trying to configure the Universal App Links files (assetlinks.json for Android, apple-app-site-association for iOS) but I’m having some issues here.

The files are being served and I can see them, however, I’ve noticed that when running Caddy locally the files will come up as application/json but when being served from the production server, these files have mime text/plain.

In order to solve this, I’ve tried a few different approaches, being the most recent one this file:

http://{$APP_HOST} {
	redir 301 {
		if {scheme} is http
		https://{host}{uri}
	}
}
https://{$APP_HOST} {
	gzip
	tls {$APP_CRT} {$APP_KEY}
	header / Vary "Accept-Language, Cookie"
	rewrite {
		if {file} not favicon.ico
		if {file} not manifest.json
		if {file} not service-worker.js
		if {path} not_has /assets
		to {path} {path}/ /index.html
	}
	root {$APP_ROOT}
	log stdout
	errors stdout
}
https://{$APP_HOST}/.well-known/ {
	gzip
	tls {$APP_CRT} {$APP_KEY}
	header /.well-known Content-Type application/json
	mime .* application/json
	root {$APP_ROOT}/.well-known
	log stdout
	errors stdout
}

First, I’d just like to have the .well-known path in the same definition as the https host. Tried using rewrites and proxy but none of those solutions seemed to work. I’m always redirected into index.html.

But, the worst part is that I’ve been unable to explicitly declare the file types to be application/json nor the mime types.

For context, these files are placed at /var/www/html/.well-known dir, where there are two files: assetlinks.json and apple-app-site-association (notice the lack of extension, as per Apple’s requirements ).

So,
1 - How come there are differences in type from running Caddy locally or on prod server?
2 - And why can’t I force the type to be json for all files under .well-know?
3 - How can I avoid defining a “new site” just for the .well-known files?

I really need this to work flawlessly.

Best Regards,
Celso Santos

mime .* application/json

Dunno if mime supports globs? Looks like the way its designed wouldn’t work with Apple’s no-extension-requirement anyway… Nice, Apple :confused:

There really shouldn’t be… Caddy’s a single binary. If caddy -version is the same and the init system and launch commands are the same and the Caddyfile is identical it should behave identically. Any differences must be attributed to the environment.

Should be doable. Set the header directly using base path matching instead of relying on per-extension via mime:

header /.well-known Content-Type application/json

Chuck that header directive in the main vhost block. That rewrite looks good to me at a glance, but if you’re always getting rewritten, it might need some troubleshooting. I’ve done similar stuff in the past with good effect, though.

Hello,

@Whitestrake thank you for your reply.

Been doing some more tests, applied the changes suggested by @Whitestrake but I’m still getting the same behaviour and for the life of me, I can’t understand why there are these differences.

Here’s my updated Caddyfile:

http://{$APP_HOST} {
	redir 301 {
		if {scheme} is http
		https://{host}{uri}
	}
}
https://{$APP_HOST} {
	gzip
	tls {$APP_CRT} {$APP_KEY}
	header / Vary "Accept-Language, Cookie"
	header /.well-known Content-Type application/json
	rewrite {
		if {file} not favicon.ico
		if {file} not manifest.json
		if {file} not service-worker.js
		if {path} not_has /.well-known
		if {path} not_has /assets
		to {path} {path}/ /index.html
	}
	root {$APP_ROOT}
	log stdout
	errors stdout
}

And here are some screenshots of the behaviour I’m mentioning:

(Sorry, had to post a dropbox link as I can’t upload more than 1 image or more than 2 links)

Can anyone help sort this out please?

Hrm. What are the differences between your dev and prod environment?

Locally I run Caddy directly on my machine, which is OSX.

Remotely, it’s running on Docker/Kubernetes based on the zzrot/alpine-caddy:v0.9.5 Docker image.

Also, running Docker on my machine, the content-type is ok, it’s still showing application/json (added a local_docker.jpg file to the Dropbox folder)

Quick thought while I’m investigating - you can simplify your Caddyfile even further.

Your first vhost has a redundant if statement - the scheme will always be HTTP for directives inside a http:// vhost.

Also, the behaviour you’ve specified is identical to Automatic HTTPS defaults, so you could remove the entire first vhost and simplify the second vhost to just {$APP_HOST} (sans scheme).

Contrary to popular belief programs in Golang should not have an empty container; they expect and read some files, such as:

  • /etc/mime.types (→ go:src/mime/type.go)
  • /etc/services
  • /etc/resolv.conf
  • /etc/ssl/certs/ca-certificates.crt

I guess the contents of the first aforementioned file differ in production from your development environment.

By the way, if you’re using a libc implementation, better use glibc.

1 Like

@Whitestrake didn’t know that. Thanks.

@wmark I guess that if they were different, then running Docker locally or remotely would provide the same output? From my tests, with Docker, based on the same image, the outputs are different and not the same as expected.

EDIT

@wmark actually, there’s no mime.types on my Docker image. However, that still doesn’t explain the differences

Actually, a caveat; you’ve provided certs and keys, so Automatic HTTPS is disabled. Disregard my advice, it’s not applicable.

I was just about to edit my post mentioning that I recall having to put http because of some certificate related issue.

It was that. The HTTP endpoint is provided because I’m providing my own Certificate.

Ok, I think i figured it out.

Basically, putting "application/json" (inside quotation marks) seems to have solved it.

http://{$APP_HOST} {
	redir 301 {
		if {scheme} is http
		https://{host}{uri}
	}
}
https://{$APP_HOST} {
	gzip
	tls {$APP_CRT} {$APP_KEY}
	header / Vary "Accept-Language, Cookie"
	header /.well-known Content-Type "application/json"
	rewrite {
		if {file} not favicon.ico
		if {file} not manifest.json
		if {file} not service-worker.js
		if {path} not_has /.well-known
		if {path} not_has /assets
		to {path} {path}/ /index.html
	}
	root {$APP_ROOT}
	log stdout
	errors stdout
}
1 Like

Huh. True that, all the docs use double quoted values in the examples, perhaps this is why. Weird that it worked without them, but only on your macOS environment…

I’m intrigued. The quotes shouldn’t make a difference in this case because the value has no whitespace…

Hello @matt and @Whitestrake

I’m as intrigued as you. Especially when it comes to Docker as, in theory, it should behave exactly the same wether running locally or remotely.

The only explanation I could possibly think of is that this is somehow something the Kernel provides and Docker inherits, but I don’t fully understand how that should have any influence and to be honest highly unlikely.

For future reference, maybe we should include a note on the docs related to headers or just plainly make it mandatory to have quotes to avoid this.

Are you running Caddy directly under the macOS environment, or under Docker on Mac via the same container as your prod?

I tried both cases. Both of them worked properly.

Just not when running on Kubernetes.

When using Kubernetes, is something before Caddy? For example, does your Kubernetes setup use nginx as frontend? (A popular case.) If so, then nginx might be changing the content-type header, or different deployments have been mixed up here. (Which I currently assume.)

Hi @wmark.

Nope. No other servers before Caddy. This pod is directly exposed to the world with it’s own public IP.
This then serves one Angular 2 app.