You don’t need to run your own Mastodon instance to have an address with a custom domain.

If Alice owns wonderland.com1, there’s a way for her to say, “I’m @alice@wonderland.com on Mastodon!” instead of “I’m @alice@mastodon.social!”, even though her account is indeed hosted on mastodon.social and she’s not running her own instance.

Mastodon addresses are just like email addresses, comprised of a name and a domain, and just like you can have Google host your email with a custom domain, you can have people discover you on Mastodon with a custom domain.

Why would you want a custom domain for your Mastodon address? Because this way you can change your Mastodon provider just like you can change your email provider while keeping your address.

The addressing scheme might be the same but the technologies for configuring a custom domain differ. For email, you have to configure the MX record of your DNS provider, while for Mastodon, you need to place a specific file in a well-known location of your web server.

Mastodon implements the ActivityPub protocol with WebFinger for profile discovery. So when you want to mention or follow @alice@mastodon.social, your instance sends a request to the following endpoint (note “webfinger” in the URL):

https://mastodon.social/.well-known/webfinger?resource=acct:alice@mastodon.social

The JSON response looks like this:

{
  "subject": "acct:alice@mastodon.social",
  "aliases": [
    "https://mastodon.social/@alice",
    "https://mastodon.social/users/alice"
  ],
  "links": [
    {
      "rel": "http://webfinger.net/rel/profile-page",
      "type": "text/html",
      "href": "https://mastodon.social/@alice"
    },
    {
      "rel": "self",
      "type": "application/activity+json",
      "href": "https://mastodon.social/users/alice"
    },
    {
      "rel": "http://ostatus.org/schema/1.0/subscribe",
      "template": "https://mastodon.social/authorize_interaction?uri={uri}"
    }
  ]
}

It is from this response that your instance gathers the relevant profile information to set things up.

The Mastodon documentation states: “user profiles can be hosted either locally on the same website as yours, or remotely on a completely different website.” You already figured out where this is going: The key point here is that, the “different website” doesn’t need to be a fully functional instance, it can be your own website hosting just your profile.

For Alice to use @alice@wonderland.com, she just needs to have her web server respond to /.well-known/webfinger?resource=acct:alice@wonderland.com and return the above JSON file. Now, usually /.well-known/webfinger would be implemented as a fully-fledged webservice endpoint and if Alice wanted to set this up for the white rabbit and the red queen as well, things would start to become a bit more complicated, but for her usecase she can just ignore the query part of the request (?resource=acct:alice@wonderland.com) and place a static file in the well-known directory called webfinger. You might need to use a different name, depending on your web host, but more on that below.

So Alice copies the response she got from her instance and saves it to .well-known/webfinger in the root directory of her web site. Because she’s detail oriented, she also replaces the subject "acct:alice@mastodon.social" with "acct:alice@wonderland.com", though she is not sure it makes a difference, but it’s the proper response, so she goes with it. She republishes here site and finally updates here Twitter bio, adding:

🐘 @alice@wonderland.com

This way, people can find her on Mastodon and she doesn’t need to put the name of her hosting instance on her calling card.

Summary

  • Create an account on your Mastodon instance of choice. At the time of writing, mastodon.social is closed for new registrations, so you need to look around a bit to find an instance that suits you.
  • Web finger yourself: construct a request using the format: https://<domain>/.well-known/webfinger?resource=acct:<name>@<domain>, run a quick curl against the URL or use your web browser, and save the response to a file.
  • Move the file to .well-known/webfinger (but see below) in the public directory of your website.
  • Replace the subject account in "acct:alice@mastodon.social" with your custom own (make sure to keep the "acct:" part) so the response account matches the request. Otherwise the file should be usable as is.
  • Republish your site.

Appendix

Jekyll

If you’re using a site generator like Jekyll, you might have to tweak your template to work with your custom setup. For this site, I added the following to _config.yml:

mastodon:
- username: alex
  instance: kobachi.jp
  alias: https://toot.io/@AlexKobachiJP

Here, username and instance are existing template properties, alias is my addition, so I updated the Mastodon part of the _includes/social.html template file to make use of it:

{%- for mst in site.mastodon -%}
  {%- if mst.username and mst.instance -%}
    {%- if mst.alias -%}
      <li><a href="{{ mst.alias }}"><svg class="svg-icon"><use xlink:href="{{ '/assets/minima-social-icons.svg#mastodon' | relative_url }}"></use></svg> <span class="username">{{ mst.username|escape }}@{{ mst.instance|escape }}</span></a></li>
    {%- else -%}
      <li><a href="https://{{ mst.instance| cgi_escape | escape}}/@{{mst.username}}"><svg class="svg-icon"><use xlink:href="{{ '/assets/minima-social-icons.svg#mastodon' | relative_url }}"></use></svg> <span class="username">{{ mst.username|escape }}</span></a></li>
    {%- endif -%}
  {%- endif -%}
{%- endfor -%}

This adds a check for the custom alias ({%- if mst.alias -%}) and uses it for the link if present. Otherwise, it just falls back to the existing logic which generates the link from the given username and instance.

Further, if you stick with the dotfile name, you might need to add the following to your _config.yml to make sure the directory is included in the generated site.

include:
 - .well-known

AWS Amplify

I host this site on AWS Amplify which is in the bad habbit of interpreting request paths wihtout a path extension as directory and adding trailing slashes, so a request for /.well-known/webfinger turns into /.well-known/webfinger/ which generates a 404 even if the file webfinger exists in its proper location. Apparently Amplify needs the path extension derive the Content-Type header of the response, so the workaround that suggests itself is to just rename the file to webfinger.json and set up the following redirect rule in the AWS Amplify console:

{
  "source": "/.well-known/webfinger",
  "target": "/.well-known/webfinger.json",
  "status": "200",
   "condition": null
}
  1. At the time of writing, this domain strangely doesn’t seem to host a website…