Paste a URL into X (formerly Twitter) or Facebook, and a card appears: a title, a description, and an image, neatly assembled. That is not automatic guesswork — the page itself publishes metadata called OGP (Open Graph Protocol), and the platform’s crawler reads it to build the card. Following the previous post on JSON-LD, this one covers OGP, another piece of metadata that lives in <head>, using this blog’s implementation (wpmm.jp/blog and en.wpmm.jp/blog) as the example.
Note: OGP was proposed by Facebook in 2010. The name reflects the idea of treating a page as part of a graph — a network of connected nodes. Today it has become a de facto standard: many platforms beyond Facebook (X, LINE, Slack, and others) read the same meta tags to build their own preview cards.
The Card Is Just <meta> Tags
OGP is not exotic technology — it is a set of <meta property="og:..."> tags in <head>. Four of them are enough to form a basic card:
| Property | Role |
|---|---|
og:title |
The title shown on the card |
og:description |
The description shown on the card |
og:image |
The URL of the image shown on the card |
og:url |
The page’s canonical URL, recorded as the share source |
When a URL is posted or sent, the platform’s crawler fetches that page and reads these tags to assemble the card. This is entirely separate from how the page renders in a browser — it is preview data built specifically for sharing.
og:type — What Kind of Page Is This
og:type declares what the page represents. A blog article uses article; a general page such as a homepage uses website.
<meta property="og:type" content="article" />
Setting article also enables article-specific properties such as article:published_time and article:author. This blog outputs og:type="article" on individual post pages, and og:type="website" everywhere else (homepage, archives, search results, and so on).
Image Dimensions Matter More Than They Look
Declaring og:image:width and og:image:height alongside og:image lets a crawler settle the layout before it even fetches the image, which tends to make card assembly more reliable. A widely used size across platforms is roughly 1200×630 (landscape). This blog’s generated OGP images use that same size.
<meta property="og:image" content="https://wpmm.jp/blog/wp-content/uploads/ogp/post-52-ja-20260922010000.png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
For X specifically, it’s also worth adding twitter:card (set to summary_large_image for a large-image card), plus twitter:title / twitter:description / twitter:image. X prioritizes its own twitter: prefixed tags, so relying on OGP alone can result in a smaller card or unexpected content.
Why the Image URL Isn’t Always the Same — This Blog’s Four-Step Fallback
This blog automatically generates an image with the article’s title baked in, for posts that have no featured image set. What og:image returns follows this priority order:
- If a featured image is explicitly set on the post, use it — highest priority
- For an individual post, if a pre-generated OGP image already exists on disk (cached), return that cached file’s static URL
- For an individual post with no cache yet, point to the on-the-fly generation engine with a query-string URL (fallback)
- For anything else (homepage, archives, 404, etc.), use the site’s shared default image
// Simplified pseudocode
function wpmm_blog_ogp_image_url() {
if ( is_singular() && has_post_thumbnail() ) {
return get_the_post_thumbnail_url(); // 1) featured image first
}
if ( is_singular( 'post' ) ) {
$cached = find_cached_ogp_png( get_the_ID() );
if ( $cached ) {
return $cached; // 2) static cached URL
}
return generator_url_with_query(); // 3) fall back to dynamic generation
}
return $default_ogp_image_url; // 4) site-wide default
}
The reason steps 2 and 3 are kept separate is that a query-string URL (a dynamic URL) is not always handled correctly as an image by every social platform’s crawler. This blog actually hit that problem once: an X post went out pointing at a dynamic URL, and the crawler failed to load the image, leaving a small text-only card that never updated afterward. The fix was to have the server call the image-generation engine internally, once, at publish or update time, and save the result to disk as a file. By the time the post is shared, a static PNG with no query string already exists, and og:image can return that static URL directly. The dynamic URL only comes into play as a safety net, when cache generation has somehow failed.
Caching and Content-Type Are Part of This Too
The engine that serves the OGP image sets Content-Type: image/png explicitly and applies a fairly long Cache-Control duration. Social platforms’ own crawlers often cache what they fetch for a while too, which means a single failed fetch (a cardless card) can linger even after the underlying problem is fixed. The HTTP cache header concepts covered a few posts back apply just as directly to serving OGP images.
og:url Pins Down the “Share Source”
og:url should hold the canonical URL — no trailing slash inconsistencies, no stray query parameters. If that value drifts, the same article can end up split across multiple URLs for share-count or like-count purposes, as far as the platform is concerned. Using the same value as the canonical URL is the safe default.
// Strip the query string before using it for archives, 404s, etc.
$ogp_url = strtok( $_SERVER['REQUEST_URI'], '?' );
Verifying After Publishing
Like hreflang and JSON-LD, OGP tags are invisible on the rendered page, so gaps are easy to miss. A few ways to check:
- View the page source and confirm the
og:tags carry the expected values - Fetch just the
og:imageURL withcurland check whether it still carries a query string (a sign it fell back to the dynamic URL) - Use a platform’s own share-preview tool to see the actual card
On this blog, the SEO check script audit.py mechanically verifies that og:type / og:title / og:description / og:url / og:image are all present after each publish.
Common Pitfalls
og:imagestays a dynamic URL (with a query string): more prone to failed fetches by a platform’s crawler, which tends to produce cardless cardsog:image:width/heightare missing: not strictly required, but layout tends to settle later, making the card less predictable- A query string leaks into
og:url: the same article gets tracked as several different URLs - A platform caches a failed card for a long time: a fix does not always show up right away. Without an official way to force a recrawl, a workaround is to re-share a different URL (for example, one with an added query string)
Summary
OGP is share-specific summary data embedded in a page as <meta property="og:..."> tags. og:title, og:description, og:image, and og:url form the baseline; og:type declares what kind of page it is, and explicit image dimensions help the layout settle predictably. Because a static image URL and a dynamic one are not treated the same way by every platform’s crawler, favoring a pre-generated static URL — and keeping dynamic generation as a fallback for failure cases only — goes a long way toward avoiding broken share cards.