Running my own Mastodon instance "behind" Astro

3 min read
My profile on my Mastodon instance
My profile on my Mastodon instance

The Mastodon instance I’ve been on for the past 7 years, octodon.social is shutting down next year. So rather than try to find a replacement, I decided to run my own, single-user instance.

The issue isn’t that good, well-moderated instances don’t exist, they do! But one of the big reasons why I stayed on Octodon was because I knew the moderation team personally so I trusted their decisions and agreed with most of them. I just frankly couldn’t be bothered with having to find and vet a new instance and I like to tinker so, after some advices from my friend Niléane I went and spun up my own!

It’s up at https://social.erambert.me and you can find me on Mastodon by looking up @eramdam@erambert.me.

The setup isn’t anything too fancy:

All set up while following the official documentation.

The only quirk of my setup might is that the “local domain” (domain the instance responds to) and the “web domain” (domain where the Web interface lives) are different. Meaning that people can find me by looking up @eramdam@erambert.me despite the actual instance running on https://social.erambert.me. It’s a behavior that is well documented by Mastodon and is usually trivial to implement when you host your website on your own server…

Except I don’t do that 😅 My website is, for now, running on Astro and hosted on Vercel so I can’t just modify the web server configuration to set up the necessary redirection.

Now, I’m not the only one that tried to do this with a similar setuip link link link. But none of those solutions were satisfactory for me because:

  1. they assume only one account as the “destination” OR
  2. they work by outputing a “dumb” JSON file.

I want to have the options to maybe host more than one account on my instance so those limitations were deal-breaker. Thankfully Sean McPherson’s article put me on the right track and I ended up implementing an Astro endpoint.

All I had to was write this in a file under src/pages/.well-known/webfinger.ts, make sure my site was using the Vercel adapter to run in server mode instead of static mode and I was done!

src/pages/.well-known/webfinger.ts
import { APIRoute } from "astro";
const destinationInstance = "https://social.erambert.me";
export const GET: APIRoute = async ({ request }) => {
const url = new URL(request.url);
const newUrl = new URL(
url.toString().replace(url.origin, ""),
destinationInstance,
);
const response = await fetch(newUrl.toString());
const json = await response.json();
return new Response(JSON.stringify(json, null, 2), {
headers: {
"Content-Type": "application/jrd+json",
"Access-Control-Allow-Origin": "*",
},
});
};

Now people could query @eramdam@erambert.me and find me as if they queried @eramdam@social.erambert.me! Neat!

After having written this post, Tixie made me realize that I could use Astro’s redirect method instead of doing a fetch in the endpoint code? Both seem to work but I am a bit worried about the potentially missing Access-Control-Allow-Origin header…

src/pages/.well-known/webfinger.ts
import { APIRoute } from "astro";
export const GET: APIRoute = async ({ request, redirect }) => {
const url = new URL(request.url);
const newUrl = new URL(
url.toString().replace(url.origin, ""),
"https://social.erambert.me",
);
return redirect(newUrl.toString(), 301);
};

Update on September 3rd: It turns out, Vercel rewrites work just fine for this and I didn’t realize 😄 I added the following file to my repo and I was done

vercel.json
{
"rewrites": [
{
"source": "/.well-known/webfinger",
"destination": "https://social.erambert.me/.well-known/webfinger"
}
]
}