Paste a link into Slack, a group chat, or a social post and something appears: a headline, a sentence of summary, a picture. None of that is taken from the page a visitor sees. It is read from a handful of <meta> tags in the document head, and when those tags are wrong the link arrives as a bare grey URL no matter how good the page behind it is.
The tags belong to the Open Graph protocol, originally created at Facebook and now read by nearly every platform that renders a preview. It is a small specification, and almost every broken card traces back to a handful of things in it.
What the tags are for#
The protocol lets a page state what it is instead of leaving a platform to work it out. Without that statement a consumer has to guess, and guessing produces the previews everyone has seen: the site logo as the thumbnail because it was the first image in the markup, or a description that turns out to be cookie-banner text because that was the first paragraph.
What makes this awkward to maintain is that the tags are invisible. They never render, and nothing about a correct-looking page tells you the preview is broken. You find out when somebody shares it, which is the reason link-preview checkers exist at all, ours included at OG card preview.
The four the spec requires#
The specification names four properties as required on every page:
og:title— the title of the object as it should appear, not necessarily your<title>element, which usually carries site branding a card does not need.og:type— what kind of thing the page is. The non-vertical types includearticle,book,profileandwebsite, and there are music and video verticals with their own subtypes.og:image— an image URL representing the object.og:url— described by the spec as "the canonical URL of your object that will be used as its permanent ID in the graph."
That last definition carries more weight than its one line suggests. Because og:url is treated as an identity, the same article reachable at several addresses (a tracking-parameter version, a syndicated copy, a printer-friendly variant) should declare one shared og:url. Otherwise a platform sees several distinct objects, and any engagement counted against the link gets split between them.
Choosing article for og:type brings in a second set of properties the spec defines for that type: article:published_time, article:modified_time, article:author, article:section and article:tag. This post carries the first of those, as does every other post here.
Everything else is optional, including the one people assume is mandatory. og:description is listed as optional metadata, alongside og:site_name, og:determiner, og:audio, og:video, and the locale pair (og:locale takes the form language_TERRITORY and defaults to en_US). Optional in the spec is not optional in practice: a card without a description shows title and domain only.
Why the attribute is property, not name#
Look closely at two tags that sit inches apart in the same head.
<meta name="description" content="…"><meta property="og:description" content="…">
The attribute changes, and it is not a style choice. MDN's reference for <meta> documents charset, content, http-equiv, name and media. There is no property attribute in that list, because it comes from RDFa rather than from HTML itself. Open Graph is built on RDFa, which is why the specification's own example page opens with <html prefix="og: https://ogp.me/ns#">.
The practical consequence shows up when a template or an SEO plugin writes name="og:title" instead of property="og:title". Parsers differ in how strictly they enforce the attribute, so a mismatch like that tends to work on one platform and fail on another, and intermittent failure is much harder to track down than total failure.
Describing the image properly#
The image is the part of a card people notice, and og:image alone is the bare minimum. The spec defines six structured properties that attach to it: og:image:url, og:image:secure_url, og:image:type, og:image:width, og:image:height and og:image:alt. The same six exist for og:video; og:audio gets the first three.
Two of them earn their place immediately. Declaring og:image:width and og:image:height lets a consumer reserve the right amount of space before the file arrives, so the card does not reflow in someone's feed. And og:image:alt is Open Graph's only route to alternative text for a card image, which means that without it a screen-reader user gets a title, a domain, and silence where the picture is.
One requirement causes more blank thumbnails than any other: the value has to be an absolute URL. og:image="/card.png" is not valid, and consumers generally drop it rather than resolving it against the page. Our checker raises that as an error rather than a warning.
Dimensions are where the spec stops and convention takes over. Open Graph sets no minimum size, no maximum file size, and no aspect ratio. The widely used 1200×630 comes from platform guidance rather than from the protocol, so treat it as a good default and treat a platform's own documentation as the authority when that network renders something unexpected.
Duplicates, arrays, and which tag wins#
Repeating a property is legal. Several og:image tags form an array of candidates, and the spec settles conflicts with one sentence: "The first tag (from top to bottom) is given preference during conflicts."
Read that as a debugging rule. When a CMS writes one og:title and an SEO plugin writes another, the winner is whichever appears earlier in the source, and editing the second one changes nothing at all.
Structured properties follow the root they come after. Write og:image, then og:image:width and og:image:height, and those dimensions describe that image. A second og:image starts a new object, and structured properties after it attach to the new one. Ordering a block for tidiness rather than by grouping is how a page ends up declaring one image's dimensions for a different file.
Where X's tags fit#
X, formerly Twitter, layers its own twitter: tags over Open Graph and in practice falls back to the og: equivalents for title, description and image when its own are absent. A complete Open Graph set therefore covers it without duplicating every value.
The tag worth setting explicitly is twitter:card, which selects the shape rather than the content. It takes one of four defined values: summary, summary_large_image, app and player. Our checker treats anything outside that set as a warning, since a typo there does not break the card so much as silently drop it to a default layout. Beyond the tags, each platform applies its own truncation and cropping: a preview tool reports the values a consumer receives, not how any one of them draws them.
The crawler is not a browser#
Most remaining card failures come from assuming the thing reading your page behaves like the browser you tested in. It does not, in four ways.
It usually does not run your JavaScript. Metadata injected client-side by a framework often does not exist for a link crawler. Those tags have to be server-rendered into the HTML that arrives.
It reads a prefix of the document, not all of it. Our own reader stops at </head>, or after 256 KB if the head never closes, and says so in the results when the cap is hit. Metadata belongs early in the head, above whatever inline styles the build produces.
It follows redirects and scrapes the destination. A shortened or campaign-tagged link is read at the end of the chain, so the tags that matter are the ones on the final URL. Our guide to HTTP status codes covers what separates a 301 from a 302 and a 307.
It arrives with no session. A page that varies by login state, cookie consent, or geography shows the crawler the anonymous version, which may be a consent interstitial rather than the article.
Two more things sit outside your control once the tags are right. The image is fetched separately from wherever it is hosted, often a CDN, so hotlink protection or an authenticated bucket produces a present tag and a blank card. And platforms cache scrape results, sometimes for days. Nothing you change clears someone else's cache; the major platforms each publish a tool that forces a re-scrape, and that is the only lever.
oEmbed, the other mechanism#
Open Graph is not the only way a platform learns what a URL is. oEmbed describes itself as "a format for allowing an embedded representation of a URL on third party sites," and some consumers prefer it when offered.
The two work differently. Open Graph is a description a page carries about itself, read straight out of the markup. oEmbed is an API: the page advertises an endpoint with <link rel="alternate" type="application/json+oembed" href="…"> (or text/xml+oembed for the XML flavour), and a consumer calls that endpoint for a structured response, which always carries a type and a version of 1.0. For most sites Open Graph is the whole job. oEmbed becomes worth running when you publish something meant to be embedded rather than linked, such as a video player, because it returns embeddable HTML instead of a static description.
Checking a page before anyone shares it#
Our OG card preview fetches a URL from the server side, reads only as far as the end of the head, and resolves the fields a consumer resolves: title, description, image, url, site name, type and twitter:card. Alongside each value it names the tag that supplied it, which is the part doing the diagnostic work. A title sourced from <title> rather than og:title tells you the Open Graph tag is missing, not stale, and those two have different fixes.
It raises findings for the failure modes above: no title anywhere, no image tag, a relative image URL, a missing description, missing width and height, a missing og:image:alt, a twitter:card value outside the four, and a head that ran past the size cap. One deliberate limit is worth knowing: it never downloads the card image. It reports the URL and your own browser loads the picture directly from its origin, because a service that retrieves arbitrary images on request is an open image proxy.
Once the tags read correctly, the HTTP header inspector is a reasonable second pass over the same URL, and our post on HTTP security headers goes through what it grades.
Open Graph tags get written once, usually by a template, and then nobody looks at them again for years while the site changes underneath. Reading a page the way a machine reads it takes about a minute, and it is the only way to see the version of your link that everyone else sees.
See what your link actually shows
Read any URL's Open Graph and Twitter Card tags, see which tag supplied each value, and get the missing or malformed ones flagged before somebody shares the link.
Preview an OG card →