Appearance
WordPress Integration
Generate OG images for WordPress sites.
Plugin Approach (Recommended)
Custom Plugin
Create a simple plugin to generate OG images:
php
<?php
/**
* Plugin Name: OG Image API
* Description: Generate dynamic Open Graph images
* Version: 1.0.0
*/
// Prevent direct access
if (!defined('ABSPATH')) exit;
// Settings
define('OG_IMAGE_API_KEY', get_option('og_image_api_key', ''));
define('OG_IMAGE_API_URL', 'https://ogimageapi.io/api/generate');
/**
* Add settings page
*/
add_action('admin_menu', function() {
add_options_page(
'OG Image API Settings',
'OG Image API',
'manage_options',
'og-image-api',
'og_image_settings_page'
);
});
function og_image_settings_page() {
if (isset($_POST['og_image_api_key'])) {
update_option('og_image_api_key', sanitize_text_field($_POST['og_image_api_key']));
}
$api_key = get_option('og_image_api_key', '');
?>
<div class="wrap">
<h1>OG Image API Settings</h1>
<form method="post">
<table class="form-table">
<tr>
<th>API Key</th>
<td>
<input type="text" name="og_image_api_key" value="<?php echo esc_attr($api_key); ?>" class="regular-text">
</td>
</tr>
</table>
<?php submit_button(); ?>
</form>
</div>
<?php
}
/**
* Generate OG image for a post
*/
function generate_og_image($post_id) {
$post = get_post($post_id);
if (!$post || $post->post_status !== 'publish') return;
$api_key = get_option('og_image_api_key');
if (!$api_key) return;
// Get post data
$title = $post->post_title;
$excerpt = get_the_excerpt($post_id) ?: wp_trim_words($post->post_content, 20);
$author = get_the_author_meta('display_name', $post->post_author);
$avatar = get_avatar_url($post->post_author, ['size' => 200]);
// Determine template based on post type
$template = 'blog';
if ($post->post_type === 'product') {
$template = 'product';
}
// Make API request
$response = wp_remote_post(OG_IMAGE_API_URL, [
'headers' => [
'Content-Type' => 'application/json',
'X-API-Key' => $api_key
],
'body' => json_encode([
'title' => $title,
'subtitle' => $excerpt,
'author_name' => $author,
'author_avatar_url' => $avatar,
'template' => $template,
'theme' => 'dark'
]),
'timeout' => 30
]);
if (is_wp_error($response)) {
error_log('OG Image API error: ' . $response->get_error_message());
return;
}
// Save image
$image_data = wp_remote_retrieve_body($response);
$upload_dir = wp_upload_dir();
$og_dir = $upload_dir['basedir'] . '/og-images';
if (!file_exists($og_dir)) {
wp_mkdir_p($og_dir);
}
$file_path = $og_dir . '/' . $post_id . '.png';
file_put_contents($file_path, $image_data);
// Save URL in post meta
$file_url = $upload_dir['baseurl'] . '/og-images/' . $post_id . '.png';
update_post_meta($post_id, '_og_image_url', $file_url);
return $file_url;
}
// Generate on publish
add_action('publish_post', 'generate_og_image', 10, 1);
add_action('save_post', function($post_id) {
if (get_post_status($post_id) === 'publish') {
generate_og_image($post_id);
}
});
/**
* Output OG meta tags
*/
add_action('wp_head', function() {
if (!is_singular()) return;
$post_id = get_the_ID();
$og_image = get_post_meta($post_id, '_og_image_url', true);
// Generate if doesn't exist
if (!$og_image) {
$og_image = generate_og_image($post_id);
}
if ($og_image) {
echo '<meta property="og:image" content="' . esc_url($og_image) . '" />' . "\n";
echo '<meta property="og:image:width" content="1200" />' . "\n";
echo '<meta property="og:image:height" content="630" />' . "\n";
echo '<meta name="twitter:card" content="summary_large_image" />' . "\n";
echo '<meta name="twitter:image" content="' . esc_url($og_image) . '" />' . "\n";
}
}, 1);
/**
* Add regenerate button to post editor
*/
add_action('add_meta_boxes', function() {
add_meta_box(
'og_image_preview',
'OG Image Preview',
'og_image_meta_box',
['post', 'page'],
'side'
);
});
function og_image_meta_box($post) {
$og_image = get_post_meta($post->ID, '_og_image_url', true);
?>
<div id="og-image-preview">
<?php if ($og_image): ?>
<img src="<?php echo esc_url($og_image); ?>" style="max-width:100%;">
<?php else: ?>
<p>No OG image generated yet.</p>
<?php endif; ?>
</div>
<p>
<button type="button" class="button" id="regenerate-og-image">
Regenerate OG Image
</button>
</p>
<script>
jQuery('#regenerate-og-image').on('click', function() {
jQuery(this).prop('disabled', true).text('Generating...');
jQuery.post(ajaxurl, {
action: 'regenerate_og_image',
post_id: <?php echo $post->ID; ?>,
nonce: '<?php echo wp_create_nonce('regenerate_og_image'); ?>'
}, function(response) {
location.reload();
});
});
</script>
<?php
}
add_action('wp_ajax_regenerate_og_image', function() {
check_ajax_referer('regenerate_og_image', 'nonce');
$post_id = intval($_POST['post_id']);
$result = generate_og_image($post_id);
wp_send_json_success(['url' => $result]);
});WooCommerce Integration
Add product-specific handling:
php
/**
* Generate OG image for WooCommerce products
*/
function generate_product_og_image($post_id) {
$product = wc_get_product($post_id);
if (!$product) return;
$api_key = get_option('og_image_api_key');
if (!$api_key) return;
// Get product data
$title = $product->get_name();
$price = '$' . $product->get_price();
$original_price = $product->is_on_sale() ? '$' . $product->get_regular_price() : null;
$image_url = wp_get_attachment_url($product->get_image_id());
$rating = $product->get_average_rating();
$payload = [
'title' => $title,
'product_image_url' => $image_url,
'price' => $price,
'brand' => $product->get_attribute('brand') ?: get_bloginfo('name'),
'rating' => floatval($rating),
'template' => 'product',
'theme' => 'dark'
];
if ($original_price) {
$payload['original_price'] = $original_price;
$payload['badge'] = 'SALE';
}
$response = wp_remote_post(OG_IMAGE_API_URL, [
'headers' => [
'Content-Type' => 'application/json',
'X-API-Key' => $api_key
],
'body' => json_encode($payload),
'timeout' => 30
]);
// Save image...
// Same as above
}
add_action('woocommerce_update_product', 'generate_product_og_image');Bulk Generation
Generate images for all existing posts:
php
/**
* Add bulk generate tool
*/
add_action('admin_menu', function() {
add_management_page(
'Bulk Generate OG Images',
'Generate OG Images',
'manage_options',
'bulk-generate-og',
'bulk_generate_og_page'
);
});
function bulk_generate_og_page() {
if (isset($_POST['generate_all'])) {
$posts = get_posts([
'post_type' => ['post', 'page'],
'post_status' => 'publish',
'numberposts' => -1
]);
$count = 0;
foreach ($posts as $post) {
generate_og_image($post->ID);
$count++;
}
echo '<div class="notice notice-success"><p>Generated ' . $count . ' OG images.</p></div>';
}
?>
<div class="wrap">
<h1>Bulk Generate OG Images</h1>
<form method="post">
<p>This will generate OG images for all published posts and pages.</p>
<?php submit_button('Generate All OG Images', 'primary', 'generate_all'); ?>
</form>
</div>
<?php
}Theme Integration
If not using a plugin, add to your theme:
php
// functions.php
function theme_og_image($post_id = null) {
if (!$post_id) $post_id = get_the_ID();
$og_image = get_post_meta($post_id, '_og_image_url', true);
if (!$og_image) {
// Generate dynamically using query params
$title = urlencode(get_the_title($post_id));
$excerpt = urlencode(get_the_excerpt($post_id));
// Use a signed URL or your own proxy endpoint
$og_image = 'https://your-proxy.com/og?title=' . $title . '&subtitle=' . $excerpt;
}
return $og_image;
}Best Practices
- Cache generated images — Store in uploads folder
- Generate on publish — Not on every page load
- Use a plugin — Easier to maintain
- Handle WooCommerce separately — Products need different data
- Add admin interface — Let editors regenerate