Generate Social Media Link Preview Images from a Webpage with PHP
Generate a PNG preview image with PHP GD, publish it at a public URL, and connect it to your webpage with Open Graph metadata.
To generate a social media link preview image with PHP, create an image with PHP’s GD extension, save it at a stable public URL, then point your webpage’s Open Graph og:image metadata at that URL. Generating the image and telling social platforms to use it are separate steps.
This guide uses GD to create a PNG. It also shows how to publish the image, add the page metadata, serve a generated image directly, validate the result, and diagnose common problems.
1. Check PHP GD support
GD provides PHP functions for creating and manipulating images, but the PHP build must include GD support. Available image formats can vary with the installed GD version and its libraries. Check the deployment environment before choosing a format or deploying code. See the PHP GD documentation and its format support details.
<?php
var_dump(extension_loaded('gd'));
var_dump(function_exists('imagepng'));
?>
Run this in the same PHP environment that will generate the image. A command-line PHP installation and the PHP process behind a web server can use different configurations.
2. Generate and save a PNG with GD
This runnable script creates a 1200 × 630 PNG, draws a simple background and title, and saves it as public/previews/article.png. Create the output directory first and ensure the PHP process can write to it.
<?php
declare(strict_types=1);
if (!extension_loaded('gd') || !function_exists('imagepng')) {
http_response_code(500);
exit('PHP GD with PNG support is required.');
}
$width = 1200;
$height = 630;
$image = imagecreatetruecolor($width, $height);
if ($image === false) {
http_response_code(500);
exit('Could not allocate image.');
}
$background = imagecolorallocate($image, 20, 31, 55);
$accent = imagecolorallocate($image, 91, 192, 190);
$white = imagecolorallocate($image, 255, 255, 255);
imagefilledrectangle($image, 0, 0, $width, $height, $background);
imagefilledrectangle($image, 0, 0, 24, $height, $accent);
imagestring($image, 5, 72, 80, 'ARTICLE PREVIEW', $accent);
imagestring($image, 5, 72, 155, 'A useful page title', $white);
imagestring($image, 4, 72, 215, 'A short supporting description', $white);
$outputPath = __DIR__ . '/public/previews/article.png';
$outputDirectory = dirname($outputPath);
if (!is_dir($outputDirectory) && !mkdir($outputDirectory, 0755, true) && !is_dir($outputDirectory)) {
imagedestroy($image);
http_response_code(500);
exit('Could not create output directory.');
}
if (!imagepng($image, $outputPath)) {
imagedestroy($image);
http_response_code(500);
exit('Could not write PNG file.');
}
imagedestroy($image);
echo "Wrote {$outputPath}\n";
?>
Save it as generate-preview.php and run php generate-preview.php. The PHP imagepng manual documents writing to a file or outputting the PNG stream. The sample writes a file so the webpage can reference a stable asset URL.
The built-in bitmap font used by imagestring is intentionally simple. For more control over typography, use a font-rendering function supported by your GD build, and verify that the font file is deployed where PHP can read it. Keep user-provided text escaped or otherwise handled safely when rendering it.
3. Publish the image and add Open Graph metadata
Move or generate the file in a location served by your website, then use its full, publicly reachable URL in the page’s HTML head. For example, if public/previews/article.png is exposed at https://example.com/previews/article.png, the page can include:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>A useful page title</title>
<meta property="og:title" content="A useful page title">
<meta property="og:type" content="website">
<meta property="og:url" content="https://example.com/article">
<meta property="og:image" content="https://example.com/previews/article.png">
<meta property="og:description" content="A short summary of the page.">
</head>
<body>
<h1>A useful page title</h1>
</body>
</html>
The Open Graph Protocol defines og:title, og:type, og:image, and og:url as its four basic properties. The protocol says the first value takes preference when conflicting duplicate properties appear, so output one deliberate value for each property rather than leaving conflicting tags in a template and page component.
Use an absolute image URL and ensure a crawler can retrieve it without a logged-in session. The protocol identifies the image; it does not make a private or unreachable asset public. Check that the page response contains the metadata in its HTML head, including when your site uses a framework or template system.
4. Choose dimensions for the destination
There is no universally established image size in the sources covered here. Check the current sharing guidance for each platform where the image will appear. LinkedIn’s website-sharing guidance specifies a maximum file size of 5 MB, minimum dimensions of 1200 × 627 pixels, and a recommended 1.91:1 ratio. These are LinkedIn sharing-module values, not universal rules for every social or messaging service. See LinkedIn’s sharing module guidance.
The sample’s 1200 × 630 canvas is close to that ratio, but a production design should use the exact destination requirements and keep important content away from edges where a platform may crop it. Do not confuse guidance for website sharing with separate specifications for advertisements.
5. Stream a PNG from a PHP endpoint
If a request-time image endpoint fits your application, PHP can write the PNG directly to the response instead of saving a file. Set the response type before output and do not print debug text into the image response.
<?php
declare(strict_types=1);
if (!extension_loaded('gd') || !function_exists('imagepng')) {
http_response_code(500);
exit;
}
$image = imagecreatetruecolor(1200, 630);
if ($image === false) {
http_response_code(500);
exit;
}
$background = imagecolorallocate($image, 20, 31, 55);
$white = imagecolorallocate($image, 255, 255, 255);
imagefilledrectangle($image, 0, 0, 1200, 630, $background);
imagestring($image, 5, 60, 60, 'Generated preview', $white);
header('Content-Type: image/png');
imagepng($image);
imagedestroy($image);
?>
Then point og:image at the endpoint’s public URL. A saved asset gives you a persistent file URL and lets your web server handle image delivery; a stream lets the application generate output per request. Which approach performs better depends on your application, how often the image changes, and how you cache it. If the image is based on a particular page, make sure the endpoint reliably returns that page’s intended image to anonymous crawlers.
6. Keep generated previews reliable
- Generate at publish time when possible. Saving the result avoids repeating the same drawing work for every preview request.
- Use a stable public URL. The page metadata must point to the actual asset location, and the file must remain available.
- Keep formats aligned. This example writes PNG; verify the deployed GD build supports the chosen output format before switching.
- Avoid conflicting metadata. Inspect the rendered HTML and remove duplicate tags with different values.
- Control file size and dimensions. Check destination guidance, particularly when publishing to more than one platform.
- Do not expose writable paths carelessly. Validate any dynamic filename or page identifier used to choose an output path so a request cannot write outside the intended asset directory.
Image generation uses memory for the image canvas and CPU for drawing and encoding. Larger dimensions and request-time generation increase work per request. The research sources establish GD’s image capabilities and PNG output behavior, but do not provide performance benchmarks; measure your own workload if generation volume or latency matters.
7. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
undefined function imagecreatetruecolor() |
GD is unavailable in the PHP runtime handling the script. | Enable/install GD for that runtime and confirm with extension_loaded('gd'). Check the web-server PHP configuration separately from CLI PHP. |
| PNG function is missing or writing fails | The PHP/GD build may not have the required format support, or the destination is not writable. | Check function_exists('imagepng'), verify build support, create the output directory, and grant the PHP process the needed write access. |
| The image is broken or contains text before the image | Warnings, whitespace, a byte-order mark, or debug output was sent before PNG bytes. | For a streaming endpoint, send only the image response after headers; log diagnostics separately and ensure no output precedes the PHP opening tag. |
| The preview shows no image | The og:image value may be a relative URL, the asset may be private, or the URL may not serve the generated file. |
Use the full public URL, request the asset without authentication, and verify the page’s rendered head includes the expected tag. |
| A different title or image appears | Duplicate or conflicting Open Graph tags may be present, or the page template may emit metadata in an unexpected order. | Inspect the final HTML source and emit one intended value per property. The Open Graph Protocol gives precedence to the first conflicting value. |
| Image is rejected or appears cropped | The file may exceed a destination’s limits or have unsuitable dimensions or ratio. | Check that platform’s current sharing specifications. For LinkedIn website sharing, use at least 1200 × 627 pixels, a recommended 1.91:1 ratio, and a file no larger than 5 MB. |
Or skip the browser setup
If the image you need is a screenshot of a webpage rather than a designed graphic, ScreenshotNeo returns a screenshot from one API request. See the ScreenshotNeo API documentation for request options.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
- Cookie banners, newsletter popups, and chat widgets are removed before the shot; each cleanup step can be turned off.
- Bot checks, blank pages, failed loads, timeouts, and cache hits are never billed. Response headers report the page verdict and billing status.
- An MCP server gives AI agents tools to take screenshots, get page information, and capture PDFs.
- 1,000 screenshots a month are free with no card; paid plans start at $5 for 3,000 screenshots.
Sign up for 1,000 free screenshots a month, with no card required.
FAQ
Does creating the PNG automatically create a social preview?
No. The page must also identify the image in its Open Graph metadata, especially with og:image.
Do I need a PHP image library beyond GD?
Not for this example. GD is the PHP image route shown here, provided the deployed PHP build includes the needed support.
Can one image serve every platform?
You can reference one image, but the available specifications vary by platform. Verify each destination’s current guidance before treating one canvas as suitable everywhere.


