For awhile now, I've wanted to set up an installation of GoToSocial for my lmorchard.com domain and run my own tiny fediverse outpost. And what I really wanted to do was to be able to host handles like @links@lmorchard.com, @blog@lmorchard.com, and @lmorchard@lmorchard.com. (I was thinking of doing @me@lmorchard.com, just like my email address. But, that could be confusing, because I might look like my name is "me" everywhere.)
Per the GoToSocial documentation, Split-domain deployments are supported by way of a few server-side redirects on the vanity account domain:
The way ActivityPub implementations discover how to map your account domain to your host domain is through a protocol called webfinger. This mapping is typically cached by servers and hence why you can't change it after the fact.
It works by doing a request to
https://<account domain>/.well-known/webfinger?resource=acct:@me@example.org. At this point, a server can return a redirect to where the actual webfinger endpoint is,https://<host domain>/.well-known/webfinger?resource=acct:@me@example.orgor may respond directly. The JSON document that is returned informs you what the endpoint to query is for the user
So, I need lmorchard.com/.well-known/webfinger to redirect to gts.lmorchard.com/.well-known/webfinger with query parameters intact to make the magic happen.
There's a wrinkle, though: lmorchard.com points at a statically-generated site, uploaded to Amazon S3, hosted behind a CloudFront CDN. That's been low-hassle to keep running for years now, as opposed to say a full-featured nginx server. The trade-off has been that this hosting arrangement didn't support any smarts on the server side. So, I thought the redirects would be infeasible.
However, I'd missed that CloudFront added support for edge functions a few years ago. That means redirects are entirely feasible these days!
Long story short, here's the edge function I came up with to do the needful for GoToSocial. Nothing super-special, just that a) it works and b) it took me a few rounds of mistakes before I got it working. So, this might be handy for someone else trying to do something similar! (Or me, if I ever lose it and need to set this up again.)
function handler(event) {
var request = event.request;
var uri = request.uri;
// Check if the request is for one of the well-known endpoints
if (uri === '/.well-known/webfinger' ||
uri === '/.well-known/host-meta' ||
uri === '/.well-known/nodeinfo') {
// Build redirect URL
var redirectUrl = 'https://gts.lmorchard.com' + uri;
// Manually build query string from querystring object
var queryString = request.querystring;
if (queryString && Object.keys(queryString).length > 0) {
var params = [];
for (var key in queryString) {
if (queryString.hasOwnProperty(key)) {
var value = queryString[key].value;
// Don't re-encode - values are already URL-encoded
params.push(key + '=' + value);
}
}
if (params.length > 0) {
redirectUrl += '?' + params.join('&');
}
}
return {
statusCode: 301,
statusDescription: 'Moved Permanently',
headers: {
'location': { value: redirectUrl },
'access-control-allow-origin': { value: '*' },
'access-control-allow-methods': { value: 'GET, HEAD, OPTIONS' },
'access-control-allow-headers': { value: 'Content-Type' }
}
};
}
// Return the request unchanged for all other paths
return request;
}