Home

/

FAQ

/

How do I implement Webmentions on my site?

How do I implement Webmentions on my site?

Step-by-step guide to adding Webmention support to your website, from beginner to advanced.

Implementing Webmentions on your site can range from simple to sophisticated, depending on your needs and technical comfort level. This guide covers options for everyone.

Quick Start: Using Webmentions.com

The fastest way to get started is using our hosted service:

Step 1: Sign Up

  1. Visit webmentions.com
  2. Create an account and add your domain
  3. Get your Webmention endpoint

Step 2: Add the Endpoints

Add this to your HTML <head>:

<!-- Webmention endpoint (required) -->
<link rel="webmention" href="https://receive.webmentions.com/yourdomain.com" />

<!-- Pingback endpoint (optional - for legacy WordPress sites) -->
<link rel="pingback" href="https://receive.webmentions.com/xmlrpc/yourdomain.com" />

Replace yourdomain.com with your actual domain.

The pingback endpoint is optional but recommended - it captures mentions from older blogs that haven't adopted Webmentions yet. Both types appear unified in your dashboard.

Step 3: Display Mentions

Fetch and display mentions using our API:

fetch('https://api.webmentions.com/mentions?target=https://yourdomain.com/post')
  .then(res => res.json())
  .then(data => {
    data.mentions.forEach(mention => {
      // Display each mention
      console.log(mention);
    });
  });

That's it! Your site can now receive Webmentions.

Intermediate: Static Site Integration

For static sites (Hugo, Jekyll, Eleventy, etc.), you can integrate Webmentions at build time.

Jekyll Example

  1. Install the plugin:

    gem install jekyll-webmention_io
    
  2. Configure in _config.yml:

    webmentions:
      username: yourdomain.com
    
  3. Display in templates:

    {% webmentions page.url %}
    

Eleventy Example

  1. Install the plugin:

    npm install @chrisburnell/eleventy-plugin-webmentions
    
  2. Configure in .eleventy.js:

    const webmentions = require('@chrisburnell/eleventy-plugin-webmentions');
    
    module.exports = function(config) {
      config.addPlugin(webmentions, {
        domain: 'https://yourdomain.com',
        token: process.env.WEBMENTION_IO_TOKEN
      });
    };
    
  3. Use in templates:

    {% webmentions page.url %}
    

Advanced: Custom Implementation

For full control, implement your own Webmention endpoint.

Server Setup (Node.js)

1. Create the Endpoint:

const express = require('express');
const fetch = require('node-fetch');
const app = express();

app.post('/webmention', express.urlencoded({ extended: true }), async (req, res) => {
  const { source, target } = req.body;

  // Validate parameters
  if (!isValidUrl(source) || !isValidUrl(target)) {
    return res.status(400).send('Invalid URLs');
  }

  // Check if target belongs to this site
  if (!target.startsWith('https://yourdomain.com')) {
    return res.status(400).send('Target not on this domain');
  }

  // Queue for asynchronous verification
  await queue.add('verify-webmention', { source, target });

  // Return 202 Accepted
  res.status(202).send('Webmention accepted for verification');
});

2. Implement Verification:

async function verifyWebmention(source, target) {
  try {
    // Fetch the source page
    const response = await fetch(source, {
      headers: { 'User-Agent': 'YourWebmentionBot/1.0' }
    });

    if (!response.ok) {
      throw new Error('Failed to fetch source');
    }

    const html = await response.text();

    // Check if target URL exists in source
    if (!html.includes(target)) {
      throw new Error('Target not found in source');
    }

    // Parse microformats
    const mf2 = await parseMicroformats(html, source);

    // Store the verified mention
    await saveMention({
      source,
      target,
      verified: true,
      data: mf2,
      receivedAt: new Date()
    });

    return true;
  } catch (error) {
    console.error('Verification failed:', error);
    return false;
  }
}

3. Parse Microformats:

const microformats = require('microformats-parser');

async function parseMicroformats(html, url) {
  const mf2 = microformats.get({ html, baseUrl: url });

  // Extract h-entry data
  const entry = mf2.items.find(item => item.type.includes('h-entry'));

  if (!entry) {
    return null;
  }

  return {
    type: detectMentionType(entry),
    author: extractAuthor(entry),
    content: extractContent(entry),
    published: entry.properties.published?.[0],
    url: entry.properties.url?.[0] || url
  };
}

function detectMentionType(entry) {
  const props = entry.properties;

  if (props['in-reply-to']) return 'reply';
  if (props['like-of']) return 'like';
  if (props['repost-of']) return 'repost';
  if (props['bookmark-of']) return 'bookmark';

  return 'mention';
}

Database Schema

Store mentions in your database:

CREATE TABLE webmentions (
  id SERIAL PRIMARY KEY,
  source VARCHAR(500) NOT NULL,
  target VARCHAR(500) NOT NULL,
  verified BOOLEAN DEFAULT false,
  mention_type VARCHAR(50),
  author_name VARCHAR(200),
  author_url VARCHAR(500),
  author_photo VARCHAR(500),
  content TEXT,
  published_at TIMESTAMP,
  received_at TIMESTAMP DEFAULT NOW(),
  moderation_status VARCHAR(50) DEFAULT 'pending'
);

CREATE INDEX idx_target ON webmentions(target);
CREATE INDEX idx_verified ON webmentions(verified);

Sending Webmentions

To send Webmentions when you publish content:

Automatic Sending

const cheerio = require('cheerio');

async function sendWebmentionsForPost(postUrl, postHtml) {
  const $ = cheerio.load(postHtml);
  const links = $('a[href]').map((i, el) => $(el).attr('href')).get();

  // Filter external links
  const externalLinks = links.filter(link =>
    link.startsWith('http') && !link.includes('yourdomain.com')
  );

  // Send Webmention to each
  for (const targetUrl of externalLinks) {
    try {
      await sendWebmention(postUrl, targetUrl);
    } catch (error) {
      console.error(`Failed to send Webmention to ${targetUrl}:`, error);
    }
  }
}

async function sendWebmention(source, target) {
  // Discover endpoint
  const endpoint = await discoverWebmentionEndpoint(target);

  if (!endpoint) {
    console.log(`No Webmention endpoint found for ${target}`);
    return;
  }

  // Send the mention
  const response = await fetch(endpoint, {
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body: new URLSearchParams({ source, target })
  });

  return response.status === 202 || response.status === 200;
}

Endpoint Discovery

async function discoverWebmentionEndpoint(url) {
  const response = await fetch(url);

  // Check Link header
  const linkHeader = response.headers.get('link');
  if (linkHeader) {
    const match = linkHeader.match(/<([^>]+)>;\s*rel="webmention"/);
    if (match) {
      return new URL(match[1], url).href;
    }
  }

  // Check HTML
  const html = await response.text();
  const $ = cheerio.load(html);
  const linkEl = $('link[rel="webmention"]').first();

  if (linkEl.length) {
    return new URL(linkEl.attr('href'), url).href;
  }

  return null;
}

Security Best Practices

Implement these security measures:

  1. Rate Limiting:

    const rateLimit = require('express-rate-limit');
    
    const limiter = rateLimit({
      windowMs: 15 * 60 * 1000, // 15 minutes
      max: 100 // limit each IP to 100 requests per windowMs
    });
    
    app.use('/webmention', limiter);
    
  2. Validation:

    • Verify URLs are properly formed
    • Check target belongs to your domain
    • Ensure source is publicly accessible
    • Validate source contains target link
  3. Spam Prevention:

    • Implement moderation queue
    • Use blocklists for known spam domains
    • Require manual approval for first-time domains
    • Check for suspicious patterns
  4. Content Sanitization:

    • Strip dangerous HTML/JavaScript
    • Limit content length
    • Validate image URLs
    • Escape output when displaying

Testing Your Implementation

Use these resources:

  • Webmentions.com: Our hosted service for receiving, sending, and managing mentions - the easiest way to test your implementation
  • webmention.rocks: Comprehensive test suite for validating your implementation against the W3C spec
  • Telegraph: Alternative manual Webmention sender

Next Steps

Once you have basic Webmentions working:

  1. Add microformats to your content
  2. Implement richer display of mentions
  3. Set up automatic sending
  4. Add moderation workflows
  5. Integrate with social media via Bridgy

The IndieWeb wiki has extensive documentation and examples to help you along the way.

Related FAQs

How do I add Webmentions to a static site?

Implementing Webmentions on static sites built with Jekyll, Hugo, Eleventy, Next.js, and other generators.

How do I add Webmentions to WordPress?

Complete guide to implementing Webmentions on WordPress sites using plugins and custom solutions.

How do I handle spam Webmentions?

Strategies and techniques for preventing, detecting, and moderating spam Webmentions on your website.

By

Commune Software