<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE rfc [
  <!ENTITY nbsp "&#160;">
  <!ENTITY zwsp "&#8203;">
  <!ENTITY nbhy "&#8209;">
  <!ENTITY wj "&#8288;">
]>
<rfc xmlns:xi="http://www.w3.org/2001/XInclude"
     version="3"
     category="std"
     docName="draft-borshchahivskyi-httpbis-preview-00"
     ipr="trust200902"
     submissionType="IETF"
     consensus="true"
     xml:lang="en"
     tocInclude="true"
     sortRefs="true"
     symRefs="true">

  <front>
    <title abbrev="HTTP Preview Field">The Preview HTTP Response Header Field</title>
    <seriesInfo name="Internet-Draft" value="draft-borshchahivskyi-httpbis-preview-00"/>

    <author fullname="Petro Borshchahivskyi" initials="P." surname="Borshchahivskyi">
      <address>
        <postal>
          <city>Las Vegas</city>
          <region>NV</region>
          <country>United States of America</country>
        </postal>
        <email>dev@liksu.com</email>
      </address>
    </author>

    <date year="2026" month="October" day="19"/>

    <area>Web and Internet Transport</area>
    <workgroup>HTTP</workgroup>

    <keyword>link preview</keyword>
    <keyword>unfurl</keyword>
    <keyword>HEAD</keyword>
    <keyword>Open Graph</keyword>
    <keyword>structured fields</keyword>

    <abstract>
      <t>Many applications display a "preview card" for a link: a title, a
      short description and an image. Today this information is obtained by
      retrieving the full HTML representation of the linked resource and
      extracting metadata from it. This forces origin servers to generate
      complete pages for clients that need only a few short strings.</t>
      <t>This document defines the Preview HTTP response header field, a
      Structured Field that carries link preview metadata, and describes how
      it can be delivered in responses to HEAD requests so that preview
      generators can obtain it without the origin server generating the
      representation.</t>
    </abstract>

    <note removeInRFC="true">
      <name>About This Document</name>
      <t>Discussion of this document is expected to take place on the HTTP
      Working Group mailing list (ietf-http-wg@w3.org). Open issues are listed
      in <xref target="open-issues"/>.</t>
    </note>
  </front>

  <middle>

    <section anchor="intro">
      <name>Introduction</name>
      <t>When a URI is shared in a messaging application, a social network, a
      chat tool or a collaboration product, the application commonly shows a
      preview card for it. The card is built from metadata published by the
      resource owner, most often using the Open Graph protocol <xref
      target="OGP"/>, which places <tt>meta</tt> elements in the
      <tt>head</tt> of an HTML document.</t>
      <t>To read those elements, a preview generator retrieves the resource
      with GET, although it needs only a few hundred bytes of the
      result. This has costs at every step:</t>
      <ul>
        <li>The origin server generates the representation. For a dynamically
        generated page, this usually includes database queries and template
        rendering.</li>
        <li>The representation is transferred over the network and can be
        stored by intermediaries along the way.</li>
        <li>The preview generator waits for the representation and parses
        HTML to find the metadata.</li>
      </ul>
      <t>Many generators stop reading once the <tt>head</tt> has been parsed
      or a size limit is reached, or request only the beginning of the
      representation with a Range header field (<xref target="HTTP"
      section="14.2"/>). This reduces the amount of data
      transferred, but often not the work of the origin server: unless the
      server streams its responses, the whole page has typically been
      generated before the first byte is sent.</t>
      <t>Publishing the metadata in a separate document, as oEmbed <xref
      target="OEMBED"/> and similar mechanisms do, avoids parsing HTML but
      requires a second request and a second resource to maintain.</t>
      <t>The cost is multiplied when the same link is previewed by many
      independent generators. Decentralized social networks are a documented
      example: each server that receives a post fetches the linked page
      itself, so a single popular post can cause hundreds or thousands of
      near-simultaneous requests to the origin <xref
      target="MASTODON-4486"/>.</t>
      <t>Origin servers that want to reduce this load today have to recognize
      preview generators by their User-Agent strings and serve them special
      content. This is fragile, undocumented and different for every
      generator.</t>
      <t>This document defines the Preview response header field (<xref
      target="field"/>), which carries preview metadata as a Structured Field
      <xref target="STRUCTURED-FIELDS"/>. Because the information is carried
      in a header field, it can be delivered in a response to a HEAD request
      (<xref target="HTTP" section="9.3.2"/>). An origin server can then
      answer preview generators from a lightweight handler, and generators can
      build a card from a single HEAD exchange.</t>

      <section anchor="goals">
        <name>Goals and Non-Goals</name>
        <t>The goals of this specification are:</t>
        <ul>
          <li>to let an origin server provide preview metadata without
          generating the full representation of a resource;</li>
          <li>to let a preview generator obtain that metadata with a single
          HEAD request, reducing server work, network transfer and latency at
          the same time;</li>
          <li>to remain fully backward compatible: a server or generator that
          does not implement this specification continues to work as it does
          today;</li>
          <li>to cover resources that have no HTML representation at all, such
          as PDF documents, media files or API responses.</li>
        </ul>
        <t>It is not a goal of this specification to replace the Open Graph
        protocol, to define how a preview card is rendered, or to define a
        complete vocabulary of resource metadata. Only the small set of
        properties that are needed for a typical preview card is defined.</t>
      </section>

      <section anchor="notational-conventions">
        <name>Notational Conventions</name>
        <t>The key words "<bcp14>MUST</bcp14>", "<bcp14>MUST NOT</bcp14>",
        "<bcp14>REQUIRED</bcp14>", "<bcp14>SHALL</bcp14>", "<bcp14>SHALL
        NOT</bcp14>", "<bcp14>SHOULD</bcp14>", "<bcp14>SHOULD NOT</bcp14>",
        "<bcp14>RECOMMENDED</bcp14>", "<bcp14>NOT RECOMMENDED</bcp14>",
        "<bcp14>MAY</bcp14>", and "<bcp14>OPTIONAL</bcp14>" in this document
        are to be interpreted as described in BCP&nbsp;14 <xref
        target="RFC2119"/> <xref target="RFC8174"/> when, and only when, they
        appear in all capitals, as shown here.</t>
        <t>This document uses the following terminology from <xref
        target="STRUCTURED-FIELDS" section="3"/> to specify syntax and parsing:
        Dictionary, String, Display String, Integer, Boolean and
        Parameters.</t>
        <t>The terms "origin server", "target URI", "representation",
        "selected representation" and "intermediary" are used as defined in
        <xref target="HTTP"/>.</t>
        <t>A "preview generator" is any client that retrieves information
        about a resource in order to display a preview of a link to that
        resource. A preview generator can be a server (for example, the
        backend of a messaging service) or a user agent (for example, a
        messaging application running on the device of the user who shares
        the link).</t>
      </section>
    </section>

    <section anchor="field">
      <name>The Preview Response Header Field</name>
      <t>The Preview response header field describes the resource identified
      by the target URI for the purpose of displaying a preview of a link to
      it. It is a Structured Header Field (<xref target="STRUCTURED-FIELDS"
      section="2"/>) whose value is a Dictionary.</t>
      <t>The following example shows a Preview field with a title, a
      description, an image and a site name. It is sent as four field lines,
      which recipients combine into one Dictionary as described in <xref
      target="HTTP" section="5.3"/>:</t>
      <sourcecode type="http-message"><![CDATA[
Preview: title="How we count cloud costs"
Preview: description="A practical model for small teams"
Preview: image="/og/123.jpg";width=1200;height=630
Preview: site="Example Blog"
]]></sourcecode>
      <t>The same value can be sent in a single field line, with the members
      separated by commas.</t>
      <t>Text values that contain characters outside the printable ASCII
      range are sent as Display Strings (<xref target="STRUCTURED-FIELDS"
      section="3.3.8"/>). For example, the Ukrainian word "Pryvit" ("Hello")
      written in Cyrillic script is sent as follows:</t>
      <sourcecode type="http-message"><![CDATA[
Preview: title=%"%d0%9f%d1%80%d0%b8%d0%b2%d1%96%d1%82"
]]></sourcecode>

      <section anchor="members">
        <name>Dictionary Members</name>
        <t>This section defines the members of the Preview Dictionary. All
        members are optional. A sender <bcp14>SHOULD</bcp14> include at least
        the "title" member, unless it sends the "nopreview" member.</t>

        <section anchor="m-title">
          <name>title</name>
          <t>The "title" member carries the title of the resource as it is
          intended to be displayed in a preview. Its value <bcp14>MUST</bcp14>
          be a String or a Display String. It is equivalent to the
          <tt>og:title</tt> property of <xref target="OGP"/>.</t>
        </section>

        <section anchor="m-description">
          <name>description</name>
          <t>The "description" member carries a short description of the
          resource. Its value <bcp14>MUST</bcp14> be a String or a Display
          String. It is equivalent to the <tt>og:description</tt>
          property.</t>
        </section>

        <section anchor="m-image">
          <name>image</name>
          <t>The "image" member identifies an image that represents the
          resource. Its value <bcp14>MUST</bcp14> be a String containing a
          URI-reference (<xref target="URI" section="4.1"/>). A relative
          reference is resolved as described in <xref target="resolution"/>.
          It is equivalent to the <tt>og:image</tt> property.</t>
          <t>The following Parameters are defined for the "image" member:</t>
          <dl>
            <dt>width:</dt>
            <dd>A positive Integer giving the width of the image in pixels.
            Equivalent to <tt>og:image:width</tt>.</dd>
            <dt>height:</dt>
            <dd>A positive Integer giving the height of the image in pixels.
            Equivalent to <tt>og:image:height</tt>.</dd>
            <dt>type:</dt>
            <dd>A String or a Token containing the media type of the image
            (for example, "image/jpeg"). Equivalent to
            <tt>og:image:type</tt>.</dd>
            <dt>alt:</dt>
            <dd>A String or Display String containing a textual alternative
            for the image. Equivalent to <tt>og:image:alt</tt>.</dd>
          </dl>
          <t>Providing "width" and "height" allows a preview generator to lay
          out a card before the image has been retrieved.</t>
        </section>

        <section anchor="m-site">
          <name>site</name>
          <t>The "site" member carries the name of the site or service that
          the resource belongs to. Its value <bcp14>MUST</bcp14> be a String or
          a Display String. It is equivalent to the <tt>og:site_name</tt>
          property.</t>
        </section>

        <section anchor="m-nopreview">
          <name>nopreview</name>
          <t>The "nopreview" member, when its value is the Boolean true,
          indicates that the resource owner prefers that no rich preview be
          displayed for links to this resource. In serialized form, this is
          the bare key:</t>
          <sourcecode type="http-message"><![CDATA[
Preview: nopreview
]]></sourcecode>
          <t>When "nopreview" is true, the other members of the field carry no
          meaning and recipients ignore them. A value of false has the same
          effect as the absence of the member. <xref target="consumer"/>
          describes how preview generators react to it.</t>
          <t>This member allows an origin server to decline preview generation
          cheaply, without serving the representation to every preview
          generator. It expresses a preference about previews shown to people
          who share or receive a link. It is independent of directives for
          search engines, such as "nosnippet" in robots meta tags or the
          X-Robots-Tag field, and is not a substitute for mechanisms that
          control crawling or indexing.</t>
        </section>
      </section>

      <section anchor="error-handling">
        <name>Error Handling</name>
        <t>If the field value fails to parse as a Dictionary (<xref
        target="STRUCTURED-FIELDS" section="4.2"/>), the entire field is
        ignored. When a key appears more than once, the last value is used,
        as specified for Dictionaries in <xref target="STRUCTURED-FIELDS"
        section="4.2.2"/>.</t>
        <t>As permitted by <xref target="STRUCTURED-FIELDS" section="2.2"/>,
        this specification defines its own handling of values that parse
        correctly but violate the constraints above: a member whose value
        does not have the required type (for example, a "title" whose value
        is an Integer) <bcp14>MUST</bcp14> be ignored on its own, and the
        remaining members <bcp14>MUST</bcp14> still be processed. The same
        applies to a Parameter of "image" whose value does not have the
        required type or is not positive where this is required.</t>
        <t>Recipients <bcp14>MUST</bcp14> ignore Dictionary members and
        Parameters that they do not recognize. This allows the field to be
        extended in the future.</t>
      </section>

      <section anchor="resolution">
        <name>Resolving the Image Reference</name>
        <t>A relative reference in the "image" member is resolved (<xref
        target="URI" section="5"/>) using the target URI of the request whose
        response carried the Preview field as the base URI. When redirects
        were followed, this is the target URI of the final request, not of the
        original one. The Content-Location header field (<xref target="HTTP"
        section="8.7"/>) does not affect the base URI or the resource that the
        field describes. Likewise, a preview generator uses the Preview field
        of the response it received, and does not need to follow a
        "canonical" link (<xref target="RFC6596"/>) to obtain it.</t>
      </section>
    </section>

    <section anchor="server">
      <name>Server Behavior</name>

      <section anchor="server-head">
        <name>Responses to HEAD and GET</name>
        <t>An origin server <bcp14>MAY</bcp14> send the Preview field in
        2xx (Successful) responses to HEAD and GET requests. Recipients ignore
        the field in responses with other status codes.</t>
        <t>The main benefit of the field comes from HEAD: a server can
        produce the field from a handler that does not generate the
        representation data, and a preview generator can use it without
        retrieving that data.</t>
        <t>Consistent with <xref target="HTTP" section="9.3.2"/>, an origin
        server that sends Preview in responses to HEAD <bcp14>SHOULD</bcp14>
        send the same field value in responses to GET for the same resource.
        This matters in practice for three reasons:</t>
        <ul>
          <li>some intermediaries forward HEAD requests to the origin server as
          GET requests (see <xref target="operational"/>);</li>
          <li>caches often answer HEAD requests from stored GET responses (see
          <xref target="caching"/>);</li>
          <li>some preview generators only send GET requests.</li>
        </ul>
        <t>Sending the field in GET responses does not require generating
        anything new: the server already has this information when it
        generates the representation.</t>
        <t>When a preview generator receives different Preview values for the
        same resource in responses to HEAD and GET, the value from the more
        recent response takes precedence.</t>
      </section>

      <section anchor="server-content">
        <name>Field Content</name>
        <t>Senders <bcp14>MUST</bcp14> generate the field value using the
        serialization algorithms of <xref target="STRUCTURED-FIELDS"
        section="4.1"/>. This is especially important because titles and
        descriptions are often derived from user-provided content; correct
        serialization prevents the injection of additional members or field
        lines.</t>
        <t>Senders <bcp14>SHOULD</bcp14> use a String rather than a Display
        String for text that contains only printable ASCII characters, since
        it is shorter and easier to read.</t>
        <t>Percent-encoding makes non-ASCII text considerably longer: a Cyrillic
        character, for instance, occupies two octets in UTF-8 and six
        characters after encoding. Many servers and intermediaries limit the
        total size of response header fields, sometimes to a few kilobytes,
        and fail the whole response when the limit is exceeded. Therefore:</t>
        <ul>
          <li>the serialized field value <bcp14>SHOULD NOT</bcp14> exceed 2048
          octets;</li>
          <li>a "title" <bcp14>SHOULD NOT</bcp14> exceed 100 Unicode code
          points, and a "description" <bcp14>SHOULD NOT</bcp14> exceed 300
          Unicode code points, both counted before encoding.</li>
        </ul>
        <t>For text in scripts that need several octets per code point, the
        2048-octet limit is reached before the code point limits. In that
        case, senders <bcp14>SHOULD</bcp14> shorten the "description" first.
        Senders that truncate text <bcp14>MUST</bcp14> do so on code point
        boundaries, because a Display String that contains invalid UTF-8
        causes the entire field to fail parsing (<xref
        target="STRUCTURED-FIELDS" section="4.2.10"/>).</t>
      </section>

      <section anchor="server-lang">
        <name>Language and Variants</name>
        <t>The language of the text in the Preview field is indicated by the
        Content-Language header field (<xref target="HTTP" section="8.5"/>) of
        the same response, when present. This document does not define a
        separate language member.</t>
        <t>If the content of the Preview field depends on request header
        fields, such as Accept-Language, the server <bcp14>SHOULD</bcp14> send
        a Vary header field as described in <xref target="HTTP"
        section="12.5.5"/>.</t>
      </section>

      <section anchor="server-privacy">
        <name>Access Control</name>
        <t>The Preview field can carry metadata that is not contained in the
        representation itself, for example a title and description of a PDF
        document stored in a separate database. This is expected. However, a
        server <bcp14>MUST NOT</bcp14> include in the Preview field any
        information that the requester is not authorized to obtain; the
        field is not a way around the access control that applies to the
        resource. In particular, a server that requires authentication for a
        resource <bcp14>MUST NOT</bcp14> reveal the title or description of
        that resource in a Preview field sent to an unauthenticated
        client. Responses that carry Preview for access-controlled resources
        <bcp14>SHOULD</bcp14> be marked with "Cache-Control: private" (<xref
        target="CACHING" section="5.2.2.7"/>).</t>
      </section>
    </section>

    <section anchor="consumer">
      <name>Preview Generator Behavior</name>
      <t>A preview generator can obtain the Preview field in two ways: from a
      response to the GET request it already sends today, or from a response
      to a HEAD request sent instead of that GET. The first way costs nothing
      extra; the second one realizes the savings this specification aims for,
      but only when the origin server supports the field.</t>

      <section anchor="consumer-get">
        <name>Using Preview in GET Responses</name>
        <t>A preview generator <bcp14>SHOULD</bcp14> use the Preview field
        when it finds it in a response to a GET request, following the rules
        in <xref target="consumer-processing"/>.</t>
      </section>

      <section anchor="consumer-head">
        <name>HEAD-First Retrieval</name>
        <t>A preview generator that expects the origin server to support the
        Preview field (see <xref target="consumer-discovery"/>) processes a
        link as follows:</t>
        <ol>
          <li>It sends a HEAD request for the target URI, following redirects
          as it would for a GET request.</li>
          <li>If the final response has a 2xx (Successful) status code and
          contains a Preview field that parses successfully, it processes the
          field as described in <xref target="consumer-processing"/>.</li>
          <li>In all other cases (the field is absent or invalid, contains
          neither "nopreview" nor "title", the HEAD request fails, or the
          server responds with a status code such as 405 (Method Not Allowed)
          or 501 (Not Implemented)), it falls back to the methods it uses
          today, such as retrieving the representation with GET and reading
          Open Graph metadata from it.</li>
        </ol>
      </section>

      <section anchor="consumer-processing">
        <name>Processing the Field</name>
        <ul>
          <li>If the "nopreview" member is true, the generator <bcp14>SHOULD
          NOT</bcp14> display a rich preview and <bcp14>SHOULD NOT</bcp14>
          retrieve the resource or the image for the purpose of generating
          one. It can still display the link itself.</li>
          <li>Otherwise, if the field contains a "title" member, the generator
          has enough information for a basic preview. After a HEAD request, it
          <bcp14>SHOULD NOT</bcp14> send a GET request for the target URI for
          the purpose of generating the preview. If an "image" member is
          present, the generator can retrieve the image as it would retrieve
          an <tt>og:image</tt>, subject to <xref target="security"/>. A
          generator that needs a property the field does not provide (for
          example, an image required by its card layout) <bcp14>MAY</bcp14>
          still retrieve the representation, accepting the cost that this
          specification aims to avoid.</li>
        </ul>
        <t>When a preview generator obtains both a Preview field and other
        preview metadata for the same resource (for example, Open Graph
        metadata from a representation), the information in the Preview
        field <bcp14>SHOULD</bcp14> take precedence, member by member.
        Metadata from other sources can be used for properties that the
        Preview field does not provide.</t>
      </section>

      <section anchor="consumer-discovery">
        <name>Discovering Support</name>
        <t>Sending HEAD first to an origin server that does not support the
        field adds a round trip. Before this specification is widely
        deployed, that would be the common case. How a generator decides
        when to use HEAD-first retrieval is an implementation choice; this
        section describes a strategy that avoids most of the extra cost.</t>
        <ol>
          <li>By default, the generator retrieves links with GET as it does
          today, and uses the Preview field if the GET response contains it
          (<xref target="consumer-get"/>).</li>
          <li>When a response contains a Preview field that parses
          successfully and contains a "title" member or a "nopreview" member
          that is true, the generator records that the origin of the final
          response, after redirects, supports it, and uses HEAD-first
          retrieval for further links to that origin.</li>
          <li>When a HEAD response from such an origin does not contain such
          a field (the field is absent, invalid, or contains neither member),
          the generator falls back to GET and can remove or narrow the
          record.</li>
        </ol>
        <t>Records of support <bcp14>SHOULD</bcp14> expire after a limited
        time, because support can change. A generator can keep records at a
        finer granularity than the origin, for example per path prefix,
        because different parts of a site are sometimes served by different
        software. A stateless generator can skip discovery entirely and use
        the field only when it appears in GET responses.</t>
        <t>This strategy finds only servers that send the field in GET
        responses, as recommended in <xref target="server-head"/>. A
        generator <bcp14>MAY</bcp14> also try HEAD-first retrieval at other
        times, for example occasionally for origins with no record, to find
        servers that send the field only in HEAD responses.</t>
        <t>With the strategy above, and without occasional HEAD-first
        attempts, origins that do not support the field receive no
        additional requests, and origins that support it receive fewer
        expensive ones. Occasional attempts add a small number of HEAD
        requests to origins that do not support the field. Origins that support the field only for some of
        their resources can receive an occasional extra HEAD request.</t>
      </section>
    </section>

    <section anchor="caching">
      <name>Caching Considerations</name>
      <t>The Preview field does not change the cacheability of a response.
      The usual rules of <xref target="CACHING"/> apply.</t>
      <t>Caches often answer HEAD requests from stored GET responses. When
      the origin server includes Preview in GET responses, as recommended in
      <xref target="server-head"/>, such caches can provide the field to
      preview generators without contacting the origin server.</t>
      <t>A cache that forwards a HEAD request uses the response to update or
      invalidate its stored GET responses (<xref target="CACHING"
      section="4.3.5"/>), depending on whether the validators and the
      Content-Length match. A lightweight HEAD handler that sends values
      different from those of GET responses would make caches discard their
      stored responses and increase the load on the origin server.
      Therefore:</t>
      <ul>
        <li>a HEAD response that carries Preview <bcp14>SHOULD</bcp14> carry
        the same validator fields (<xref target="HTTP" section="8.8"/>) that
        a GET response for the same resource would carry, when the server can
        determine them without generating the representation (for example,
        from the modification time of the underlying data);</li>
        <li>a server that cannot determine a validator or the Content-Length
        without generating the representation <bcp14>SHOULD</bcp14> omit that
        field from the HEAD response rather than send a value that differs
        from the GET response, as allowed by <xref target="HTTP"
        section="9.3.2"/>.</li>
      </ul>
    </section>

    <section anchor="operational">
      <name>Operational Considerations</name>
      <t>Many HTTP server frameworks implement HEAD by running the handler
      for GET and discarding the content. Such servers send the Preview field
      correctly but do not save any work. To benefit from this specification,
      a server needs a separate HEAD handler, or a GET handler that stops
      before generating content when the request method is HEAD.</t>
      <t>Some intermediaries, including some content delivery networks,
      forward HEAD requests to the origin server as GET requests. Behind such
      an intermediary, the origin server generates the representation, and
      the preview generator receives the Preview field only if the server
      sends it in GET responses. The intermediary can still serve later
      requests from its cache.</t>
      <t>Implementations of Structured Fields that predate <xref
      target="STRUCTURED-FIELDS"/> do not support Display Strings, and fail
      to parse the entire field when one is present. Senders
      <bcp14>MAY</bcp14> use Strings for "site" and for the "alt" Parameter
      when the text can reasonably be expressed in ASCII, to reduce the
      impact of such implementations.</t>
      <t>Header compression in HTTP/2 and HTTP/3 is effective for field values
      that repeat across responses. The Preview field is usually different for
      each resource and therefore compresses poorly. Servers that send it in
      every GET response add its full size to each of those responses, most
      of which go to clients that do not need it. <xref target="server-head"/>
      recommends sending the field in GET responses because caches, some
      intermediaries and support discovery (<xref
      target="consumer-discovery"/>) depend on it. A server for which the
      added size matters more can send the field only in HEAD responses; it
      remains interoperable, but preview generators are less likely to
      discover and use it.</t>
    </section>

    <section anchor="related">
      <name>Relationship to Other Mechanisms</name>

      <section anchor="rel-ogp">
        <name>Open Graph Protocol</name>
        <t>The Preview field is designed as a transport for the subset of
        Open Graph properties <xref target="OGP"/> that preview cards use.
        <xref target="ogp-map"/> shows the correspondence. Other Open Graph
        properties already have HTTP equivalents, also shown in the table.</t>
        <table anchor="ogp-map">
          <name>Open Graph Properties and Their HTTP Equivalents</name>
          <thead>
            <tr><th>Open Graph</th><th>HTTP</th></tr>
          </thead>
          <tbody>
            <tr><td>og:title</td><td>Preview: title</td></tr>
            <tr><td>og:description</td><td>Preview: description</td></tr>
            <tr><td>og:image</td><td>Preview: image</td></tr>
            <tr><td>og:image:width, og:image:height, og:image:type,
            og:image:alt</td><td>Parameters of Preview: image</td></tr>
            <tr><td>og:site_name</td><td>Preview: site</td></tr>
            <tr><td>og:url</td><td>Link with rel="canonical" <xref
            target="RFC6596"/></td></tr>
            <tr><td>og:locale</td><td>Content-Language</td></tr>
          </tbody>
        </table>
        <t>A resource can publish both Open Graph metadata and the Preview
        field. Publishers are encouraged to keep them consistent.</t>
      </section>

      <section anchor="rel-link">
        <name>The "preview" Link Relation</name>
        <t>The "preview" link relation type <xref target="RFC6903"/> refers to
        a resource that provides a preview of the link's context, and can be
        sent in a Link header field <xref target="WEB-LINKING"/>. It can
        identify an image. Target attributes such as "title" (<xref
        target="WEB-LINKING" section="3.4.1"/>) describe the link target, not
        the resource that the response describes, and there are no attributes
        for a description, image dimensions or alternative text. A preview
        generator <bcp14>MAY</bcp14> use an image identified by a
        "preview" link when the Preview field has no "image" member.</t>
      </section>

      <section anchor="rel-conneg">
        <name>Content Negotiation</name>
        <t>An alternative design would let a preview generator request a
        special media type that contains only preview metadata, using the
        Accept header field <xref target="SOCIALHUB-CONNEG"/>. This
        specification does not take that approach because it requires a new
        media type; it requires origin servers to send "Vary: Accept", which
        reduces the efficiency of shared caches for all clients of the
        resource; and it changes the representation that is returned, whereas
        HEAD already has the semantics of "metadata without content".</t>
      </section>

      <section anchor="rel-other">
        <name>Other Mechanisms</name>
        <t>oEmbed <xref target="OEMBED"/> lets a provider return embeddable
        content from a separate endpoint, which can be discovered through a
        link with the "alternate" relation type and an oEmbed media type.
        Discovery through a Link header field is possible with HEAD, but
        retrieving the metadata then takes a second request, and an endpoint
        has to be deployed for it. oEmbed serves rich embeds from selected
        providers rather than generic preview cards, and is complementary to
        this specification.</t>
        <t>More generally, a Link header field with a relation type such as
        "describedby" could point to a separate metadata document for the
        resource. The Link Preview Manifest proposal <xref
        target="LINK-PREVIEW-MANIFEST"/> follows this approach for the same
        problem: a HEAD response carries a link to a JSON document with the
        preview metadata. Such a design can describe richer metadata, but it
        has the same cost as oEmbed discovery: a second request, and a second
        resource to deploy and keep consistent. Carrying a few short values
        directly in the response avoids both. The two approaches can coexist:
        a server can send the Preview field and also link to a richer
        document.</t>
        <t>Signposting <xref target="SIGNPOSTING"/> uses typed links in HTTP
        header fields, retrievable with HEAD, to expose metadata about
        scholarly resources. It shows that clients can successfully rely on
        HEAD responses to discover information about a resource without
        retrieving it.</t>
        <t>Fediverse Enhancement Proposal 8967 <xref target="FEP-8967"/> lets
        the author of a social media post attach preview information to the
        post itself. It reduces repeated fetching between servers but not the
        initial fetch from the origin; the two approaches can be combined.</t>
      </section>
    </section>

    <section anchor="security">
      <name>Security Considerations</name>
      <t>The Preview field contains information provided by the origin
      server, in the same way as Open Graph metadata in an HTML document. It
      therefore has similar security properties. In particular, a server can
      describe a resource in a misleading way. This was already possible by
      serving different HTML to preview generators; the Preview field does not
      add this capability, but makes it cheaper. Preview generators
      <bcp14>SHOULD</bcp14> apply the same trust policies to the Preview field
      that they apply to other publisher-provided metadata. A generator that
      suspects misleading metadata, for example because of reports from its
      users, can retrieve the representation and compare.</t>
      <t>Preview generators retrieve URIs chosen by other parties: the
      target URI chosen by the user who shares a link, every redirect target,
      and the image chosen by the origin server. Server-based generators are
      therefore exposed to server-side request forgery, both for the HEAD
      request defined here and for image retrieval, in the same way as they
      are today with GET requests and <tt>og:image</tt>. They
      <bcp14>SHOULD</bcp14> apply to every request, including each redirect
      and each image retrieval, the same restrictions that they apply today,
      such as refusing private, loopback and link-local addresses and
      checking the address after DNS resolution to prevent DNS rebinding. In
      addition, they <bcp14>SHOULD</bcp14> only retrieve images with "https"
      URIs.</t>
      <t>Intermediaries can add, remove or modify the Preview field. Using
      HTTPS protects it in transit between the origin server and the preview
      generator.</t>
      <t>Text in the field is frequently derived from user-provided content.
      <xref target="server-content"/> requires correct Structured Field
      serialization to prevent header injection. Preview generators
      <bcp14>MUST</bcp14> treat the text as untrusted and escape it
      appropriately before displaying it, for example before inserting it
      into HTML.</t>
      <t>Display Strings can contain any Unicode text, including control
      characters and bidirectional formatting characters that can make
      displayed text misleading. Senders <bcp14>SHOULD NOT</bcp14> include
      control characters (Unicode general category Cc) or bidirectional
      formatting characters in Preview text. Preview generators
      <bcp14>SHOULD</bcp14> remove such characters, or isolate the text so
      that they cannot affect the surrounding content, before display.</t>
      <t>A large Preview field can consume resources of the recipient.
      Preview generators <bcp14>SHOULD</bcp14> impose a limit on the size of
      the field value they process, and <bcp14>MAY</bcp14> truncate text
      values for display.</t>
    </section>

    <section anchor="privacy">
      <name>Privacy Considerations</name>
      <t>The Preview field carries the kind of information that publishers
      already expose for link previews through Open Graph metadata. For
      resources without an HTML representation, it can expose metadata that
      is not contained in the representation itself; the publisher decides
      what to include. <xref target="server-privacy"/> prevents servers from
      using the field to reveal information to requesters that are not
      authorized to obtain it.</t>
      <t>Some preview generators run on the device of the user who shares a
      link, so that the service they use does not learn which links they
      share. For those generators, retrieving a short HEAD response instead of
      a full page reduces data usage and delay, but the origin server still
      learns the network address of the device, as it does today.</t>
      <t>The URI in the "image" member can contain identifiers that let the
      origin server, or the server hosting the image, observe which preview
      generators displayed a link. This is also true of <tt>og:image</tt>
      today.</t>
      <t>This specification reduces the cost of each request made by preview
      generators, not the number of those requests. In particular, it does
      not change how often decentralized services retrieve the same
      link.</t>
    </section>

    <section anchor="iana">
      <name>IANA Considerations</name>
      <t>IANA is requested to register the following entry in the "Hypertext
      Transfer Protocol (HTTP) Field Name Registry" defined in <xref
      target="HTTP" section="16.3.1"/>:</t>
      <dl spacing="compact">
        <dt>Field Name:</dt><dd>Preview</dd>
        <dt>Status:</dt><dd>permanent</dd>
        <dt>Structured Type:</dt><dd>Dictionary</dd>
        <dt>Reference:</dt><dd>This document</dd>
        <dt>Comments:</dt><dd>(none)</dd>
      </dl>
    </section>

  </middle>

  <back>
      <references>
        <name>Normative References</name>

        <reference anchor="RFC2119" target="https://www.rfc-editor.org/info/rfc2119">
          <front>
            <title>Key words for use in RFCs to Indicate Requirement Levels</title>
            <author fullname="S. Bradner" initials="S." surname="Bradner"/>
            <date month="March" year="1997"/>
          </front>
          <seriesInfo name="BCP" value="14"/>
          <seriesInfo name="RFC" value="2119"/>
          <seriesInfo name="DOI" value="10.17487/RFC2119"/>
        </reference>

        <reference anchor="RFC8174" target="https://www.rfc-editor.org/info/rfc8174">
          <front>
            <title>Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words</title>
            <author fullname="B. Leiba" initials="B." surname="Leiba"/>
            <date month="May" year="2017"/>
          </front>
          <seriesInfo name="BCP" value="14"/>
          <seriesInfo name="RFC" value="8174"/>
          <seriesInfo name="DOI" value="10.17487/RFC8174"/>
        </reference>

        <reference anchor="HTTP" target="https://www.rfc-editor.org/info/rfc9110">
          <front>
            <title>HTTP Semantics</title>
            <author fullname="R. Fielding" initials="R." role="editor" surname="Fielding"/>
            <author fullname="M. Nottingham" initials="M." role="editor" surname="Nottingham"/>
            <author fullname="J. Reschke" initials="J." role="editor" surname="Reschke"/>
            <date month="June" year="2022"/>
          </front>
          <seriesInfo name="STD" value="97"/>
          <seriesInfo name="RFC" value="9110"/>
          <seriesInfo name="DOI" value="10.17487/RFC9110"/>
        </reference>

        <reference anchor="CACHING" target="https://www.rfc-editor.org/info/rfc9111">
          <front>
            <title>HTTP Caching</title>
            <author fullname="R. Fielding" initials="R." role="editor" surname="Fielding"/>
            <author fullname="M. Nottingham" initials="M." role="editor" surname="Nottingham"/>
            <author fullname="J. Reschke" initials="J." role="editor" surname="Reschke"/>
            <date month="June" year="2022"/>
          </front>
          <seriesInfo name="STD" value="98"/>
          <seriesInfo name="RFC" value="9111"/>
          <seriesInfo name="DOI" value="10.17487/RFC9111"/>
        </reference>

        <reference anchor="STRUCTURED-FIELDS" target="https://www.rfc-editor.org/info/rfc9651">
          <front>
            <title>Structured Field Values for HTTP</title>
            <author fullname="M. Nottingham" initials="M." surname="Nottingham"/>
            <author fullname="P-H. Kamp" initials="P-H." surname="Kamp"/>
            <date month="September" year="2024"/>
          </front>
          <seriesInfo name="RFC" value="9651"/>
          <seriesInfo name="DOI" value="10.17487/RFC9651"/>
        </reference>

        <reference anchor="URI" target="https://www.rfc-editor.org/info/rfc3986">
          <front>
            <title>Uniform Resource Identifier (URI): Generic Syntax</title>
            <author fullname="T. Berners-Lee" initials="T." surname="Berners-Lee"/>
            <author fullname="R. Fielding" initials="R." surname="Fielding"/>
            <author fullname="L. Masinter" initials="L." surname="Masinter"/>
            <date month="January" year="2005"/>
          </front>
          <seriesInfo name="STD" value="66"/>
          <seriesInfo name="RFC" value="3986"/>
          <seriesInfo name="DOI" value="10.17487/RFC3986"/>
        </reference>
      </references>

      <references>
        <name>Informative References</name>

        <reference anchor="WEB-LINKING" target="https://www.rfc-editor.org/info/rfc8288">
          <front>
            <title>Web Linking</title>
            <author fullname="M. Nottingham" initials="M." surname="Nottingham"/>
            <date month="October" year="2017"/>
          </front>
          <seriesInfo name="RFC" value="8288"/>
          <seriesInfo name="DOI" value="10.17487/RFC8288"/>
        </reference>

        <reference anchor="RFC6903" target="https://www.rfc-editor.org/info/rfc6903">
          <front>
            <title>Additional Link Relation Types</title>
            <author fullname="J. Snell" initials="J." surname="Snell"/>
            <date month="March" year="2013"/>
          </front>
          <seriesInfo name="RFC" value="6903"/>
          <seriesInfo name="DOI" value="10.17487/RFC6903"/>
        </reference>

        <reference anchor="RFC6596" target="https://www.rfc-editor.org/info/rfc6596">
          <front>
            <title>The Canonical Link Relation</title>
            <author fullname="M. Ohye" initials="M." surname="Ohye"/>
            <author fullname="J. Kupke" initials="J." surname="Kupke"/>
            <date month="April" year="2012"/>
          </front>
          <seriesInfo name="RFC" value="6596"/>
          <seriesInfo name="DOI" value="10.17487/RFC6596"/>
        </reference>

        <reference anchor="RFC7942" target="https://www.rfc-editor.org/info/rfc7942">
          <front>
            <title>Improving Awareness of Running Code: The Implementation Status Section</title>
            <author fullname="Y. Sheffer" initials="Y." surname="Sheffer"/>
            <author fullname="A. Farrel" initials="A." surname="Farrel"/>
            <date month="July" year="2016"/>
          </front>
          <seriesInfo name="BCP" value="205"/>
          <seriesInfo name="RFC" value="7942"/>
          <seriesInfo name="DOI" value="10.17487/RFC7942"/>
        </reference>

        <reference anchor="OGP" target="https://ogp.me/">
          <front>
            <title>The Open Graph protocol</title>
            <author><organization>Meta Platforms, Inc.</organization></author>
          </front>
        </reference>

        <reference anchor="OEMBED" target="https://oembed.com/">
          <front>
            <title>oEmbed</title>
            <author><organization>oEmbed</organization></author>
          </front>
        </reference>

        <reference anchor="SIGNPOSTING" target="https://signposting.org/">
          <front>
            <title>Signposting the Scholarly Web</title>
            <author fullname="H. Van de Sompel" initials="H." surname="Van de Sompel"/>
            <author fullname="M. L. Nelson" initials="M. L." surname="Nelson"/>
          </front>
        </reference>

        <reference anchor="FEP-8967" target="https://w3id.org/fep/8967">
          <front>
            <title>FEP-8967: Generating link previews for attached links</title>
            <author><organization>Fediverse Enhancement Proposals</organization></author>
          </front>
        </reference>

        <reference anchor="SOCIALHUB-CONNEG" target="https://socialhub.activitypub.rocks/t/using-content-negotiation-for-opengraph-metadata-mitigating-the-fedi-ddos-problem/4317">
          <front>
            <title>Using content negotiation for OpenGraph metadata - mitigating the fedi DDoS problem</title>
            <author><organization>SocialHub</organization></author>
            <date month="June" year="2024"/>
          </front>
        </reference>

        <reference anchor="LINK-PREVIEW-MANIFEST" target="https://www.quippd.com/writing/2026/03/09/link-preview-manifest-a-proposal-for-the-fediverse.html">
          <front>
            <title>Link Preview Manifest: A Proposal for the Fediverse</title>
            <author fullname="Youssuff" surname="Youssuff"/>
            <date month="March" year="2026"/>
          </front>
        </reference>

        <reference anchor="MASTODON-4486" target="https://github.com/mastodon/mastodon/issues/4486">
          <front>
            <title>Mastodon can be used as a DDOS tool</title>
            <author><organization>Mastodon project issue tracker</organization></author>
            <date year="2017"/>
          </front>
          <refcontent>Issue #4486</refcontent>
        </reference>
      </references>

    <section anchor="examples">
      <name>Complete Example</name>
      <t>A preview generator sends:</t>
      <sourcecode type="http-message"><![CDATA[
HEAD /blog/cloud-costs HTTP/1.1
Host: www.example.com
User-Agent: ExamplePreviewBot/1.0
]]></sourcecode>
      <t>The origin server answers from a handler that reads the title and
      the image location from a small metadata store, without rendering the
      page:</t>
      <sourcecode type="http-message"><![CDATA[
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Content-Language: en
Last-Modified: Mon, 12 Oct 2026 08:30:00 GMT
Cache-Control: public, max-age=3600
Link: <https://www.example.com/blog/cloud-costs>; rel="canonical"
Preview: title="How we count cloud costs"
Preview: description="A practical model for small teams"
Preview: image="/og/123.jpg";width=1200;height=630;type="image/jpeg"
Preview: site="Example Blog"
]]></sourcecode>
      <t>The handler takes the Last-Modified value from the same metadata
      store, so it matches the value of a GET response. It omits
      Content-Length, which it cannot know without rendering the page (see
      <xref target="caching"/>).</t>
      <t>The preview generator builds the card from these fields, resolves
      the image reference to "https://www.example.com/og/123.jpg" and
      retrieves the image. It does not send a GET request for the page.</t>
      <t>For a resource whose owner does not want previews:</t>
      <sourcecode type="http-message"><![CDATA[
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Preview: nopreview
]]></sourcecode>
    </section>

    <section anchor="open-issues" removeInRFC="true">
      <name>Open Issues</name>
      <ul>
        <li>Should sending Preview in GET responses (<xref
        target="server-head"/>) be a requirement rather than a
        recommendation?</li>
        <li>Should a registry of Preview members be created, and with which
        registration policy?</li>
        <li>Should a member for the kind of resource (similar to
        <tt>og:type</tt>) be defined, so that generators can choose a card
        layout for articles, videos and other content?</li>
        <li>Should video and audio previews (similar to <tt>og:video</tt> and
        <tt>og:audio</tt>) be in scope?</li>
        <li>Is the precedence rule in <xref target="consumer"/> (Preview
        before HTML metadata) the right default?</li>
        <li>Are the recommended size limits in <xref target="server-content"/>
        appropriate?</li>
        <li>Is "Preview" the best field name? It is short and descriptive, but
        it could be confused with other uses of the word, such as link preview
        features of browsers or prerendering. "Preview-Card" is a possible
        alternative; "Link-Preview" is already used informally by <xref
        target="LINK-PREVIEW-MANIFEST"/>.</li>

      </ul>
    </section>

    <section anchor="impl-status" removeInRFC="true">
      <name>Implementation Status</name>
      <t>This section records the status of known implementations of the
      protocol defined by this specification at the time of posting of this
      Internet-Draft, following the approach described in <xref target="RFC7942"/>.</t>
      <t>At the time of writing, there are no known implementations.</t>
    </section>

    <section anchor="acknowledgements" numbered="false">
      <name>Acknowledgements</name>
      <t>This proposal was informed by public discussions in the fediverse
      community about the load that link previews create on origin servers,
      by the earlier work on the "preview" link relation and on
      Signposting, and by the Link Preview Manifest proposal.</t>
    </section>
  </back>
</rfc>
