What is a Webmention endpoint?
Understanding what a Webmention endpoint is, how it works, and how to set one up for your website.
A Webmention endpoint is a URL on your website that receives notifications when other sites link to your content. Think of it as a mailbox where the web delivers notifications about who's talking about your posts.
The Basics
When someone mentions your content and sends a Webmention, their system makes an HTTP POST request to your endpoint with two pieces of information:
- source: The URL of their page that mentions you
- target: The URL of your page being mentioned
Your endpoint receives this notification, verifies it's legitimate, and processes it however you choose.
How It's Discovered
Other websites need to find your endpoint before they can send you Webmentions. You advertise it in two standard ways:
1. HTML Link Element
Add this to your page's <head>:
<link rel="webmention" href="https://yourdomain.com/webmention" />
2. HTTP Link Header
Your server can send this HTTP header:
Link: <https://yourdomain.com/webmention>; rel="webmention"
Most implementations use the HTML method as it's simpler, but supporting both provides maximum compatibility.
What an Endpoint Does
A Webmention endpoint has three main responsibilities:
1. Receive the Notification
Accept POST requests with source and target parameters:
app.post('/webmention', async (req, res) => {
const { source, target } = req.body;
// Basic validation
if (!source || !target) {
return res.status(400).send('Missing required parameters');
}
// Accept the mention
res.status(202).send('Accepted');
});
The endpoint should return 202 Accepted quickly, then process asynchronously.
2. Verify the Mention
Fetch the source page and confirm it actually links to your target:
async function verifyMention(source, target) {
// Fetch the source page
const response = await fetch(source);
const html = await response.text();
// Check if target URL appears in the source
if (!html.includes(target)) {
throw new Error('Link not found');
}
return true;
}
Why verify? Anyone can send a POST request claiming they linked to you. Verification ensures the link actually exists.
3. Process and Store
Once verified, extract metadata and store the mention:
async function processMention(source, target) {
// Parse microformats for rich data
const data = await parseMetadata(source);
// Store in database
await db.mentions.insert({
source,
target,
author: data.author,
content: data.content,
type: data.type, // reply, like, repost, etc.
verified: true,
receivedAt: new Date()
});
}
Endpoint Types
Self-Hosted Endpoint
You run your own endpoint on your server:
Pros:
- Full control over processing
- Custom storage and display
- No third-party dependencies
- Privacy of mention data
Cons:
- More complex to implement
- Server maintenance required
- Must handle spam/abuse
- Need database storage
Example (Node.js/Express):
const express = require('express');
const app = express();
app.post('/webmention',
express.urlencoded({ extended: true }),
async (req, res) => {
const { source, target } = req.body;
// Validate URLs
if (!isValidUrl(source) || !isValidUrl(target)) {
return res.status(400).send('Invalid URLs');
}
// Check target is on this domain
if (!target.startsWith('https://yourdomain.com')) {
return res.status(400).send('Invalid target');
}
// Queue for processing
await queue.add({ source, target });
// Return 202 immediately
res.status(202).send('Accepted for processing');
}
);
Hosted Endpoint (Webmentions.com)
Use our hosted service at webmentions.com to handle receiving:
Pros:
- Quick setup (5 minutes)
- Full-featured API
- Spam filtering built-in
- Handles verification automatically
- Dashboard for managing mentions
Cons:
- Data stored on our servers
- API rate limits apply
Setup:
<!-- Add to your <head> -->
<link rel="webmention" href="https://receive.webmentions.com/yourdomain.com" />
Replace yourdomain.com with your actual domain. Then fetch mentions via our API:
const response = await fetch(
'https://api.webmentions.com/mentions?target=https://yourdomain.com/post'
);
const data = await response.json();
Hybrid Approach
Some sites use our hosted endpoint for receiving but process/store mentions themselves:
// Poll Webmentions.com periodically
async function syncMentions() {
const response = await fetch(
'https://api.webmentions.com/mentions?domain=yourdomain.com',
{
headers: { Authorization: `Bearer ${process.env.WEBMENTIONS_TOKEN}` }
}
);
const data = await response.json();
for (const mention of data.mentions || []) {
// Process with your own logic
await processAndStore(mention);
}
}
// Run every hour
setInterval(syncMentions, 3600000);
Endpoint URL Patterns
Your endpoint can be:
Global (handles all pages):
<link rel="webmention" href="https://yourdomain.com/webmention" />
Per-page (different endpoints for different sections):
<!-- On blog posts -->
<link rel="webmention" href="https://yourdomain.com/webmention/blog" />
<!-- On projects -->
<link rel="webmention" href="https://yourdomain.com/webmention/projects" />
Most sites use a single global endpoint for simplicity.
Security Considerations
Rate Limiting
Prevent abuse by limiting requests:
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // 100 requests per window
message: 'Too many requests'
});
app.use('/webmention', limiter);
URL Validation
Ensure source and target are valid, accessible URLs:
function isValidUrl(string) {
try {
const url = new URL(string);
return url.protocol === 'http:' || url.protocol === 'https:';
} catch {
return false;
}
}
function isValidTarget(target, allowedDomain) {
return target.startsWith(`https://${allowedDomain}`);
}
Prevent SSRF Attacks
Don't allow mentions from private IP ranges:
function isPublicUrl(url) {
const privateRanges = [
/^10\./,
/^172\.(1[6-9]|2[0-9]|3[01])\./,
/^192\.168\./,
/^127\./,
/^localhost$/
];
const hostname = new URL(url).hostname;
return !privateRanges.some(range => range.test(hostname));
}
Async Processing
Never fetch the source URL synchronously in the request handler:
// ❌ BAD: Synchronous
app.post('/webmention', async (req, res) => {
await verifyMention(req.body.source, req.body.target); // Blocks!
res.send('OK');
});
// ✅ GOOD: Asynchronous
app.post('/webmention', async (req, res) => {
await queue.add(req.body); // Queue it
res.status(202).send('Accepted'); // Return immediately
});
This prevents attackers from tying up your server with slow requests.
Testing Your Endpoint
Manual Testing with Webmentions.com
The easiest way to test your endpoint is using our Webmentions.com service:
- Sign up and add your domain
- Use our sending tools to send a test Webmention to your target URL
- Verify your endpoint received the notification
- Check the dashboard to see the mention processed
Alternative: Telegraph
You can also use Telegraph to send test Webmentions:
- Enter your target URL
- Enter a source URL (create a test page linking to your target)
- Send the Webmention
- Check your endpoint received it
Automated Testing with webmention.rocks
Visit webmention.rocks for comprehensive tests:
- Discovery tests (HTML and HTTP)
- Verification tests
- Update and deletion handling
- Error scenarios
Command Line Testing
Send a test Webmention with curl:
curl -X POST https://yourdomain.com/webmention \
-d "source=https://test.example.com/post" \
-d "target=https://yourdomain.com/article"
Expected response:
HTTP/1.1 202 Accepted
Common Endpoint Patterns
Minimal Endpoint
Just accept and log:
app.post('/webmention', (req, res) => {
console.log('Received:', req.body);
res.status(202).send('Accepted');
});
Production Endpoint
Full implementation:
app.post('/webmention', async (req, res) => {
try {
// 1. Extract and validate
const { source, target } = req.body;
if (!isValidUrl(source) || !isValidTarget(target)) {
return res.status(400).send('Invalid parameters');
}
// 2. Queue for async processing
await mentionQueue.add({
source,
target,
receivedAt: new Date(),
ip: req.ip
});
// 3. Return 202 immediately
res.status(202).send('Accepted');
} catch (error) {
console.error('Webmention error:', error);
res.status(500).send('Server error');
}
});
// Async worker processes queued mentions
mentionQueue.process(async (job) => {
const { source, target } = job.data;
// Verify the mention
const isValid = await verifyMention(source, target);
if (!isValid) return;
// Parse metadata
const data = await parseMetadata(source);
// Store in database
await saveMention({ source, target, ...data });
});
Next Steps
Once you have an endpoint running:
- Add microformats to your content for richer mentions
- Display received mentions on your pages
- Implement moderation to handle spam
- Send Webmentions when you publish content
- Monitor and maintain your endpoint
The endpoint is just the beginning—it's the foundation for building a fully interactive, decentralized web presence.
Learn More
Related FAQs
What are microformats and why do they matter for Webmentions?
Learn how microformats add semantic meaning to your content and enable rich Webmention interactions.
How do I handle spam Webmentions?
Strategies and techniques for preventing, detecting, and moderating spam Webmentions on your website.
How do I implement Webmentions on my site?
Step-by-step guide to adding Webmention support to your website, from beginner to advanced.