What Is WebFinger and Why Should You Implement It?
WebFinger (RFC 7033) is a protocol that allows clients to discover information about people or resources using only an identifier like an email address or URL. If you're building a federated application — a social network, identity provider, or any service that needs to interoperate with the Fediverse — implementing a WebFinger endpoint is one of the first steps.
When someone on Mastodon searches for user@yourdomain.com, Mastodon sends a WebFinger request to your server to find out where that user's ActivityPub profile lives. Without a WebFinger endpoint, your users are invisible to the rest of the federated web.
How WebFinger Requests Work
WebFinger requests are simple HTTPS GET requests to:
GET /.well-known/webfinger?resource=acct:user@yourdomain.com
Host: yourdomain.com
Accept: application/jrd+json
Your server must respond with a JRD (JSON Resource Descriptor) — a JSON document describing the requested resource and providing links to related information.
The JRD Response Format
A valid WebFinger response for an ActivityPub-compatible user looks like this:
{
"subject": "acct:alice@yourdomain.com",
"aliases": [
"https://yourdomain.com/users/alice"
],
"links": [
{
"rel": "self",
"type": "application/activity+json",
"href": "https://yourdomain.com/users/alice"
},
{
"rel": "http://webfinger.net/rel/profile-page",
"type": "text/html",
"href": "https://yourdomain.com/@alice"
},
{
"rel": "http://webfinger.net/rel/avatar",
"type": "image/jpeg",
"href": "https://yourdomain.com/avatars/alice.jpg"
}
]
}
Step-by-Step Implementation
Step 1: Set Up the Route
Register a route for /.well-known/webfinger in your web framework. Here's an example in Node.js with Express:
app.get('/.well-known/webfinger', async (req, res) => {
const resource = req.query.resource;
if (!resource || !resource.startsWith('acct:')) {
return res.status(400).json({ error: 'Invalid resource' });
}
const [username, domain] = resource.slice(5).split('@');
// look up user and build response...
});
Step 2: Parse and Validate the Resource
Always validate that the requested domain matches your own domain. Reject requests for users on other domains — your server is only authoritative for its own domain.
Step 3: Look Up the User
Query your database for the username. Return a 404 if the user doesn't exist. This prevents information leakage about valid vs. invalid accounts (though some implementations choose to always return 404 for non-existent users anyway).
Step 4: Build and Return the JRD
Construct the JRD object with the correct subject, aliases, and links, then return it with the correct content type:
res.setHeader('Content-Type', 'application/jrd+json');
res.setHeader('Access-Control-Allow-Origin', '*');
res.json(jrdObject);
Common Mistakes to Avoid
- Wrong Content-Type — Must be
application/jrd+json, notapplication/json - Missing CORS headers — Browser-based clients need
Access-Control-Allow-Origin: * - Not handling the
acct:scheme — Resources may arrive asacct:user@domainor as a full URL - Ignoring the domain in the resource — Always validate that you're authoritative for the requested domain
- Serving over HTTP — WebFinger must be served over HTTPS; clients will reject HTTP responses
Testing Your WebFinger Endpoint
Once deployed, you can test your endpoint using curl:
curl -H "Accept: application/jrd+json" \
"https://yourdomain.com/.well-known/webfinger?resource=acct:alice@yourdomain.com"
You can also use the online tool at webfinger.net to look up accounts on your server and verify the response structure.
Next Steps
With WebFinger in place, your users can be discovered across the Fediverse. The natural next step is implementing ActivityPub Actor documents at the URLs referenced in your WebFinger responses, followed by inbox/outbox endpoints to enable actual federation of content.