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}"
}
]
}