Open Graph Image Generation: A Complete Developer Guide
Learn how to generate, host, and debug Open Graph images with complete HTML, browser automation, validation, and API examples.

Direct answer: an Open Graph image is the image URL a webpage declares in its Open Graph metadata. Generate or select an image, publish it at a stable public URL, and reference that URL with og:image in the document head. Add descriptive og:image:alt text and, when useful, the image type, dimensions, and secure URL.
Open Graph is metadata, not an image file format. The image can be a PNG, JPEG, WebP, or another format accepted by the consumer that fetches your page. The Open Graph protocol describes a webpage as a rich object in a social graph. Its required object properties are og:title, og:type, og:image, and og:url. See the Open Graph Protocol reference for the protocol definition and image properties.
1. Create the image and add Open Graph metadata
The implementation has four parts:

- Create an image that represents the page. You can design it manually, render an HTML/CSS template, or capture a generated page with a browser.
- Store the result at a stable, publicly reachable HTTPS URL.
- Add Open Graph tags inside the page’s
<head>. - Check the rendered HTML and the final image URL before publishing.
<!doctype html>
<html prefix='og: https://ogp.me/ns#'>
<head>
<meta charset='utf-8'>
<title>Example page</title>
<meta property='og:title' content='Example page'>
<meta property='og:type' content='website'>
<meta property='og:url' content='https://example.com/page'>
<meta property='og:image' content='https://example.com/share-image.jpg'>
<meta property='og:image:alt' content='A blue illustration representing the example page'>
<meta property='og:image:secure_url' content='https://example.com/share-image.jpg'>
<meta property='og:image:type' content='image/jpeg'>
<meta property='og:image:width' content='1200'>
<meta property='og:image:height' content='630'>
</head>
<body>...</body>
</html>
og:image identifies the image representing the page. The structured properties belong to the image declared immediately before them. The protocol recommends og:image:alt when an image is supplied; describe what the image contains rather than repeating a caption or marketing slogan.
2. Generate an image with HTML and CSS
HTML/CSS templates are practical when every article needs a consistent card with a title, author, category, and brand colors. Keep the source template separate from the final asset so you can regenerate images when the title changes.
<!doctype html>
<html>
<head>
<meta charset='utf-8'>
<style>
* { box-sizing: border-box; }
body { margin: 0; width: 1200px; height: 630px; font-family: Arial, sans-serif; background: #101827; color: white; }
.card { height: 100%; padding: 72px; display: flex; flex-direction: column; justify-content: space-between; }
.label { color: #8bd3ff; font-size: 26px; text-transform: uppercase; letter-spacing: 3px; }
h1 { max-width: 1000px; margin: 0; font-size: 74px; line-height: 1.05; }
.footer { font-size: 24px; color: #c9d4e5; }
</style>
</head>
<body>
<main class='card'>
<div class='label'>Engineering guide</div>
<h1>Open Graph Image Generation</h1>
<div class='footer'>example.com</div>
</main>
</body>
</html>
For programmatic generation, write the template with the page’s data, then render it at a fixed 1200×630 viewport. Avoid placing important text near the edges. Keep titles short enough to fit at the smallest expected font size, and test long words, punctuation, non-Latin scripts, and missing author names.
3. Render the template with Playwright
The following Node.js script creates a PNG from a local HTML file. Install Playwright with npm install playwright, save the template as og-card.html, and run the script.
import { chromium } from 'playwright';
const browser = await chromium.launch();
const page = await browser.newPage({
viewport: { width: 1200, height: 630 },
deviceScaleFactor: 1
});
await page.goto('file:///absolute/path/to/og-card.html', { waitUntil: 'networkidle' });
await page.screenshot({ path: 'share-image.png', type: 'png' });
await browser.close();
For a web template, use an HTTPS URL instead of a file: URL. Wait for fonts and images to finish loading before capture. If a remote font is important, host it reliably or use a system font so the output does not change between runs. Set a fixed viewport and device scale factor; otherwise the same template can produce different line wrapping and dimensions.
4. Choose dimensions, format, and hosting
The Open Graph protocol itself does not prescribe one universal image size or file-size maximum for every social platform. A 1200×630 canvas is a common engineering default, but verify the current requirements of every platform you target. Treat platform limits as deployment requirements rather than protocol rules.
| Decision | Guidance |
|---|---|
| Format | Use a widely supported format such as JPEG or PNG unless your target consumer documents WebP support. |
| URL | Use an absolute HTTPS URL that remains valid after deployment. |
| Caching | Version the filename, for example article-slug-v2.jpg, when replacing an image. |
| Accessibility | Provide og:image:alt describing visible content. |
| Security | Do not require cookies, authentication, or JavaScript for the image URL. |
Serve the image with the correct Content-Type, return a successful status, and avoid redirect chains. A crawler may fetch metadata from a server different from the browser you use to inspect the page, so public DNS, TLS, and firewall rules must permit it.
5. Declare multiple images correctly
You may repeat og:image to provide alternatives. The first image from top to bottom takes preference when there is a conflict. Keep each image’s structured properties directly after its own root declaration.
<meta property='og:image' content='https://example.com/article-dark.jpg'>
<meta property='og:image:alt' content='Dark illustration of the article topic'>
<meta property='og:image:width' content='1200'>
<meta property='og:image:height' content='630'>
<meta property='og:image' content='https://example.com/article-light.jpg'>
<meta property='og:image:alt' content='Light illustration of the article topic'>
<meta property='og:image:width' content='1200'>
<meta property='og:image:height' content='630'>
Use multiple values when you genuinely need alternate imagery. Do not interleave the dimensions for one image with the declaration of another.
6. Generate screenshots with ScreenshotNeo
If your image is an HTML/CSS composition, ScreenshotNeo can render the public template without you maintaining browser automation. It supports full-page or element capture, custom CSS and JavaScript, device and viewport settings, retina scale, waiting for a selector or network idle, and image resizing. Its API also accepts custom headers, cookies, user agents, authorization, timezone, geolocation, and caching controls.

Or skip the browser setup
Send one GET request to render a public Open Graph template:
curl -G 'https://api.screenshotneo.com/v1/shot' \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://example.com/og-card.html \
-o share.webp
import requests
r = requests.get(
'https://api.screenshotneo.com/v1/shot',
params={'access_key': 'YOUR_API_KEY', 'url': 'https://example.com/og-card.html'},
timeout=90,
)
r.raise_for_status()
open('share.webp', 'wb').write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://example.com/og-card.html' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot failed: ${res.status}`);
const buffer = Buffer.from(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('share.webp', buffer));
See the ScreenshotNeo documentation for request options. Cookie and consent banners, newsletter popups, and chat widgets are removed before the shot. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing result. ScreenshotNeo also provides an MCP server so Claude, Cursor, and other MCP clients can call take_screenshot, get_page_info, and capture_pdf. The Free plan includes 1,000 screenshots per month without a card; paid plans start at $5 for 3,000.
Create your free ScreenshotNeo account and generate up to 1,000 screenshots each month with no card.
7. Validate an Open Graph image before publishing
- Fetch the page HTML from its production URL.
- Confirm that
og:imageis in the initial HTML head, especially if crawlers do not execute your client-side framework. - Request the image URL with an HTTP client and check its status, content type, dimensions, and response body.
- Open the image without application cookies or a logged-in browser session.
- Check that the declared
og:urlis the canonical URL you intend to share. - After replacing an image, change its URL or account for consumer caching.
curl -I https://example.com/share-image.jpg
curl -s https://example.com/article | grep -E 'og:(title|type|url|image)'
8. Troubleshooting common failures
| Symptom | Likely cause | Fix |
|---|---|---|
| No image appears | The tag is missing from the server-rendered head or the URL is private. | Render the tags in initial HTML and make the image publicly reachable over HTTPS. |
| Old image remains | A social crawler cached the previous URL or response. | Use a versioned filename and validate the new URL. |
| Image is broken | Wrong content type, redirect loop, timeout, or blocked request. | Inspect with curl -I, return the actual image bytes, and remove authentication requirements. |
| Text is clipped | The title exceeds the template’s available width. | Wrap or shorten text, reduce font size within limits, and test the longest titles. |
| Wrong image is selected | Multiple og:image tags are ordered unexpectedly. |
Put the preferred image first and keep its structured properties immediately after it. |
| Screenshot has a popup | The capture occurred before consent or widget cleanup. | Accept or remove overlays before capture, wait for the page state, or use ScreenshotNeo’s cleanup behavior. |
| Fonts differ between runs | A remote font was unavailable when rendering. | Use a stable hosted font or a system fallback and wait for font loading. |
9. Performance, reliability, and cost
Generate images during publishing or in a background job rather than on every page request. Store immutable, versioned assets and serve them through your normal static-file or CDN layer. Reuse identical images for unchanged content. For browser rendering, limit external requests, wait only for the resources needed by the card, and keep the template deterministic.
When using an API, set a client timeout, retry transient network failures with backoff, and record the returned status and response headers. Cache successful output by a hash of the template inputs. ScreenshotNeo lets you choose a cache TTL; its response headers expose whether a result was a clean page and whether it was billed. Bulk capture supports up to 100 URLs per call, and asynchronous jobs can deliver signed webhooks when generation should happen outside a request path.
10. FAQ
Is an Open Graph image required?
No. It is the image a page declares for rich previews. Without it, a consumer may show no image or choose another representation.
Can the image URL be relative?
Use an absolute URL so a crawler can resolve it without relying on the page’s base URL.
Should alt text contain keywords?
No. Describe the visible image content accurately; it is metadata for the image, not a keyword field.
Can I use a different image for every article?
Yes. Generate an asset from each article’s data and give each version a stable URL.
Why does one platform show a different crop?
Consumers can apply their own rendering and cropping rules. Verify the current requirements of each target platform and keep important content inside a safe central area.
Summary checklist
- Generate a readable image at dimensions appropriate for your target platforms.
- Host it at a stable, public HTTPS URL.
- Add
og:title,og:type,og:url, andog:image. - Add
og:image:altand useful structured image properties. - Put the preferred image first when declaring multiple images.
- Validate the initial HTML, image status, content type, dimensions, and cache behavior.


