Youssuff Quips

Link Preview Manifest: A Proposal for the Fediverse

TL;DR: Starting in Firefox version 142, Mozilla released a “Link Previews” feature whose main purpose is to push AI onto its users.

While the feature is problematic, commenters post pointed out that some previews are helpful – e.g. on Wikipedia, where a preview will appear when people hover over a wiki-linked page.

Screenshot of the a link preview of the Fediverse page on Wikipedia

Other commenters pondered about some minimal way to replicate this elsewhere, and it seemed to be worth investigation. Read on as I propose an enhancement to the Fediverse (and maybe even web standards) to make Link Previews great: the Link Preview Manifest.

No video for this post, since I was boring myself as I recorded.


Introduction

Link previews are seemingly one of those web features that mostly work – until they they start to scale, and you need to figure out how the sausage is made.

The Firefox implementation shows why. There, generating a preview means retrieving a page’s HTML and parsing it, discarding most of what was downloaded. Firefox is just one client, though.

In federated systems, where a single link can fan out across thousands of timelines and clients, this behavior can accidentally lead to distributed denial-of-service (DDOS) attacks, as observed by publishers using Mastodon.

In a 2024 article, It’s FOSS asked people to not share their links on Mastodon, explaining that:

When you share a link on Mastodon, a link preview is generated for it, right?

With Mastodon being a federated platform (a part of the Fediverse), the request to generate a link preview is not generated by just one Mastodon instance. There are many instances connected to it who also initiate requests for the content almost immediately.

And, this “fediverse effect” increases the load on the website’s server in a big way.

Sure, some websites may not get overwhelmed with the requests, but Mastodon does generate numerous hits, increasing the load on the server. Especially, if the link reaches a profile with more followers (and a broader network of instances).

Things have not improved much since then.

Link Previews as generated by most user agents are expensive to create, since many of the pages being previewed are generated dynamically, increasing resource usage and page generation time.

There’s got to be a better way, and in this post, I am proposing one: Link Preview Manifest.

Please steal this idea if it is good!

A Link Preview Manifest is a small, cacheable resource that provides preview-specific metadata for a URL without requiring clients to fetch or parse the full HTML document.

Publishing software can generate this resource whenever the previewed document is created or updated once and it can be stored as a static file that does not need to be regenerated for each link preview request.

Discovery Mechanism

HTTP Headers

User agents can request a preview for a URI via a call to get the HEAD of a URI:

HEAD https://example.com/posts/hello-world/

User agents would receive some headers in response.

HTTP/1.1 200 OK
Link: <https://example.com/posts/hello-world/index.preview.json>; rel="link-preview"
Link-Preview: available
Cache-Control: max-age=86400

Upon receipt of the URI’s HTTP headers, user agents should inspect the headers for a Link relation advertising a preview resource. If a resource is advertised, the user agent can GET the preview resource, rather than the URI being previewed.

This separates discovery from retrieval and avoids the expense of page generation and transfer. This differs from the existing solutions, which require user agents to GET the entire page to parse link preview metadata.

The Link header is used since it already provides a standardized mechanism for advertising related resources.

Guessed Fallback

While the primary discovery mechanism for link previews is Link header metadata, user agents can also look for previews in the page URI + .preview.json

If publishers can’t set headers, they can instead define link previews as external resources:

<link rel="link-preview" href="index.preview.json" type="application/link-preview+json">

This is a secondary fallback, in situations where the publisher is on shared hosting or cannot update web server behavior to support updating Link headers.

Preview File

The preview JSON file is a small, cacheable and verifiable representation of a web resource, intended for link previews in clients. Its purpose is to allow clients to render previews without needing to download the actual resource, and supports identity verification for safe caching and federation.

Field Type Required Description
version integer Version of the link preview spec.
title string Human-readable headline or title of the content.
description string Short, plain-text summary of the resource.
url string Canonical URL of the resource; clients use this as the primary link target.
site object Information about the site hosting the content. Contains:
name (string, required) — site or provider name
url (string, optional) — link to site home
type string Resource type (article, video, photo, link, rich) - helps clients render appropriately.
published string (ISO 8601) Publication date of the resource. Useful for displaying timestamps.
images array of objects Array of preview images. Each object may include:
url (string, required)
width (integer, optional)
height (integer, optional)
integrity (string, optional, SRI hash)
video object Optional video preview. May include:
url (string, required)
width / height (integers)
duration (integer, seconds)
integrity (string, optional)
cache object Caching hints for clients and CDNs. Example: {"max_age": 86400} (seconds).
integrity string SRI hash of resource, allowing clients to verify authenticity. Applies to:
images (string, optional)
• entire file (string, optional)

Examples

Article With Preview Image

{
  "version": 1,
  "type": "article",
  "title": "Why Link Previews Should Be Explicit",
  "description": "An argument for a lightweight web standard that avoids HTML scraping and supports federation.",
  "url": "https://example.com/blog/explicit-link-previews",
  "site": {
    "name": "Example Blog",
    "url": "https://example.com"
  },
  "published": "2026-02-09T12:00:00Z",
  "images": [
    {
      "url": "https://example.com/media/explicit-previews.jpg",
      "width": 1200,
      "height": 630,
      "integrity": "sha256-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa="
    }
  ],
  "cache": {
    "max_age": 86400
  },
  "integrity": "sha256-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb="
}
{
  "version": 1,
  "type": "photo",
  "title": "Weekend in Kyoto",
  "description": "A short photo set from a weekend trip to Kyoto.",
  "url": "https://example.com/photos/kyoto-weekend",
  "site": {
    "name": "Example Photos",
    "url": "https://example.com"
  },
  "images": [
    {
      "url": "https://example.com/media/kyoto-1.jpg",
      "width": 1080,
      "height": 1080,
      "integrity": "sha256-ccccccccccccccccccccccccccccccccccccccccccc="
    },
    {
      "url": "https://example.com/media/kyoto-2.jpg",
      "width": 1080,
      "height": 1080,
      "integrity": "sha256-ddddddddddddddddddddddddddddddddddddddddddd="
    },
    {
      "url": "https://example.com/media/kyoto-3.jpg",
      "width": 1080,
      "height": 1080,
      "integrity": "sha256-eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee="
    }
  ],
  "cache": {
    "max_age": 43200
  },
  "integrity": "sha256-fffffffffffffffffffffffffffffffffffffffffff="
}

Video

{
  "version": 1,
  "type": "video",
  "title": "Federation Without Scraping",
  "description": "A short talk on making link previews cheaper and safer for the fediverse.",
  "url": "https://example.com/videos/federation-without-scraping",
  "site": {
    "name": "Example Videos",
    "url": "https://example.com"
  },
  "images": [
    {
      "url": "https://example.com/media/video-poster.jpg",
      "width": 1280,
      "height": 720,
      "integrity": "sha256-1111111111111111111111111111111111111111111="
    }
  ],
  "video": {
    "url": "https://example.com/media/federation-talk.mp4",
    "width": 1280,
    "height": 720,
    "duration": 315,
    "integrity": "sha256-2222222222222222222222222222222222222222222="
  },
  "cache": {
    "max_age": 86400
  },
  "integrity": "sha256-3333333333333333333333333333333333333333333="
}

Storage

Preview files can be generated statically as separate .preview.json files per page. These files can be generated at build time, alongside HTML.

On disk, this would look like:

/article/index.html
/article/index.preview.json

Client Flow

Step 1: Header-based Discovery

When a client wants to get link preview metadata:

  • Perform a HEAD request to the URL.
  • Look for a Link header with rel=”link-preview”.

Example:

Link: <https://example.com/article/index.preview.json>;
      rel="link-preview";
      type="application/link-preview+json"

If found, proceed to step 3.

Step 2: Fallback Discovery

If a link header is not present in HEAD, user agents can try (in order):

  • Predictable adjacent file
    • Example: https://example.com/article/index.preview.json
  • HTML <link> element
    • Example: <link rel="link-preview" href="index.preview.json">

Clients should stop once they successfully discover the manifest file.

Now that we know where the manifest is located, we can download it.

  • Do a GET on the discovered link preview manifest
  • Output:
    • Content-Type: application/link-preview+json
    • a small JSON file

Step 4: Verify Integrity

If present, clients can verify the Subresource Integrity hash. If fetching media resources, individual hashes can be verified.

If verification fails, clients may fall back to parsing the page.

Step 5: Cache

Clients should respect HTTP caching headers to minimize load on publishers.

Step 6: Parse and Render

At this point, the client has the Link Preview Manifest containing the information that would previously have been manually parsed from page HTML. As we now have a manifest that encapsulates this data in a verifiable way, we can avoid parsing of page data.

Conclusion

The way that web developers currently do link previews are inefficient and wasteful, and advantages big tech players like Facebook and X, since they are centralized and can easily absorb the costs associated with scraping pages.

The web deserves a better way: preview data that is explicit, lightweight, cacheable, and verifiable. By separating preview metadata from page content, clients can get and share metadata without forcing web servers into expensive page generation.

This proposal extends the web incrementally - advanced hosts can advertise Link Preview Manifests via page headers, while simpler setups can rely on predictible locations and fallbacks linked from HTML. Clients can adopt the standard incrementally without needing to throw away their existing functionality. The standard itself can be versioned to meet forthcoming needs.

It doesn’t make sense for publishers to not want their content posted on Mastodon because it causes their web servers to buckle under the strain. As more and more clients generate link previews, we can help scale the federated web by making link metadata cheap to advertise, transfer and consume.

Importantly, this approach does not require new infrastructure. It works with static site generators, shared hosting, CDNs, and existing HTTP semantics. Advanced hosts can benefit from cheap discovery via headers, while simpler setups can rely on predictable file locations and HTML fallbacks. Clients can adopt it incrementally and fall back to legacy scraping when necessary.

Subresource Integrity makes it possible to federate previews without centralized trust or authentication, allowing instances to cache and redistribute previews confidently. Combined with aggressive caching and small payloads, this shifts link previews from a repeated scraping problem into a solved distribution problem.

If link previews are going to remain a foundational part of the federated web, they should be treated as first-class resources. Making them explicit is a small change, but one that moves us toward a more resilient and scalable web.


If you liked this material, please consider supporting me. You can message me or follow this blog on Mastodon.

This project is maintained by yoasif