Getting WebFinger to Play Nicely on Nginx
I was inspired by
Scott Hanselman’s blog post on configuring WebFinger for use with Mastodon. The idea
is that you leverage a domain in your control to enable people to search for you in the
Mastodon fediverse—freeing you from anchoring yourself to a specific Mastodon instance,
and also freeing your friends and enemies to find you via an email address or really
<any-value-here>@example.com.
I wanted to make it possible to do just that using my new public email address,
karl@stolley.dev. If you search your favorite Mastodon instance for that
email address, or even ugh-that-guy@stolley.dev, you should see my @stolley@hachyderm.io username pop up
in the results.
Creating the
webfinger file and putting it in your URL’s /.well-known/
path is only part of the story, however. You need to make sure that your web server or
platform knows how to return sensible responses to requests for
/.well-known/webfinger.
I’ve got a humble flat-file setup for my WebFinger file. But to get Nginx to
serve /.well-known/webfinger correctly, I also added the following location
block for the file:
location /.well-known/webfinger {
types { } default_type 'application/jrd+json';
add_header 'Access-Control-Allow-Origin' '*';
}
The first thing that block does is ensure that the webfinger file is sent
with the correct application/jrd+json MIME type. The funny types {
} syntax, with the empty braces, overrides Nginx’s default MIME types in
mime.types and sets application/jrd+json as the default MIME
type. The call to types also has the effect of setting the
content-type HTTP header. For that reason, you should’t attempt to call
add_header to set content-type. From experimenting, I found
that it and other Nginx header-setting methods resulted in two content-type
headers (an HTTP
no-no): one content-type header with the Nginx default
application/octet-stream and the other with the correct
application/jrd+json.
The second thing that location block does is set a permissive CORS header, in keeping with the WebFinger spec (RFC 7033), specifically section 5 on CORS. That little header ensures the widest possible access to the file, including from client-side JavaScript.
To test things out, I made this little call to curl—here showing only the
HTTP response headers and contents of my webfinger file:
$ curl -v https://stolley.dev/.well-known/webfinger
# A bunch of stuff snipped here...
< HTTP/2 200
< server: nginx
< date: Wed, 21 Dec 2022 19:01:38 GMT
< content-type: application/jrd+json
< content-length: 659
< last-modified: Wed, 21 Dec 2022 17:58:22 GMT
< etag: "63a3493e-293"
< access-control-allow-origin: *
< accept-ranges: bytes
<
{
"subject":"acct:stolley@hachyderm.io",
"aliases":
[
"https://hachyderm.io/@stolley",
"https://hachyderm.io/users/stolley"
],
"links":
[
{
"rel":"http://webfinger.net/rel/profile-page",
"type":"text/html",
"href":"https://hachyderm.io/@stolley"
},
{
"rel":"self",
"type":"application/activity+json",
"href":"https://hachyderm.io/users/stolley"
},
{
"rel":"http://ostatus.org/schema/1.0/subscribe",
"template":"https://hachyderm.io/authorize_interaction?uri={uri}"
}
]
}