Appearance
Best Practices
Guidelines for optimal use of OG Image API.
Security
Protect Your API Key
javascript
// ❌ Never expose in client-side code
const response = await fetch('/api/generate', {
headers: { 'X-API-Key': 'og_secret_key' } // EXPOSED!
});
// ✅ Use server-side routes or signed URLs
const response = await fetch('/api/og?title=Hello'); // Internal routeUse Environment Variables
bash
# .env (never commit)
OG_IMAGE_API_KEY=og_your_key_herejavascript
// Access securely
const apiKey = process.env.OG_IMAGE_API_KEY;Validate Inputs
javascript
function validateOGParams(params) {
return {
title: sanitize(params.title?.slice(0, 200)),
subtitle: sanitize(params.subtitle?.slice(0, 300)),
theme: ['dark', 'light'].includes(params.theme) ? params.theme : 'dark',
template: VALID_TEMPLATES.includes(params.template) ? params.template : 'default'
};
}Performance
Generate at Build Time
For static content, generate images during build:
javascript
// build.js
async function prebuildOGImages() {
const posts = await getAllPosts();
for (const post of posts) {
await generateOGImage(post);
}
}Cache Aggressively
javascript
// Set long cache headers
res.setHeader('Cache-Control', 'public, max-age=86400, s-maxage=604800');Use CDN
Deploy your OG image route to edge locations:
javascript
// Next.js Edge runtime
export const config = { runtime: 'edge' };Optimize Source Images
- Compress images before sending URLs
- Use appropriately sized images (not 4K for avatars)
- Prefer JPEG for photos, PNG for graphics
Content Guidelines
Title Best Practices
| Do | Don't |
|---|---|
| 5-10 words | 20+ words |
| Clear value prop | Vague statements |
| Include keywords | Keyword stuffing |
| Proper capitalization | ALL CAPS everything |
Subtitle Best Practices
- Expand on the title
- Add context or benefit
- Keep under 100 characters for best display
Image URLs
javascript
// ✅ Use HTTPS
"https://example.com/image.png"
// ✅ Use optimized images
"https://cdn.example.com/image-800x800.jpg"
// ❌ Avoid
"http://example.com/image.png" // Not HTTPS
"https://example.com/huge-4k-image.png" // Too largeError Handling
Implement Fallbacks
javascript
async function getOGImage(params) {
try {
const response = await fetch('/api/og', {
method: 'POST',
body: JSON.stringify(params)
});
if (!response.ok) throw new Error('Generation failed');
return await response.arrayBuffer();
} catch (error) {
console.error('OG image error:', error);
return fs.readFileSync('./public/fallback-og.png');
}
}Log Errors
javascript
if (!response.ok) {
console.error('OG API Error', {
status: response.status,
params: sanitizeForLogging(params)
});
}Rate Limiting
Respect Limits
| Plan | Rate Limit |
|---|---|
| Free | 10/minute |
| Pro | 60/minute |
| Business | 120/minute |
Implement Backoff
javascript
async function generateWithRetry(params, attempts = 3) {
for (let i = 0; i < attempts; i++) {
const response = await fetch('/api/generate', {/*...*/});
if (response.status === 429) {
const delay = Math.pow(2, i) * 1000;
await new Promise(r => setTimeout(r, delay));
continue;
}
return response;
}
throw new Error('Rate limit exceeded');
}SEO Optimization
Complete Meta Tags
html
<meta property="og:title" content="Your Title">
<meta property="og:description" content="Your description">
<meta property="og:image" content="https://yoursite.com/og/page.png">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
<meta property="og:type" content="article">
<meta property="og:url" content="https://yoursite.com/page">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="Your Title">
<meta name="twitter:description" content="Your description">
<meta name="twitter:image" content="https://yoursite.com/og/page.png">Use Absolute URLs
html
<!-- ✅ Correct -->
<meta property="og:image" content="https://example.com/og/image.png">
<!-- ❌ Wrong -->
<meta property="og:image" content="/og/image.png">Test Your Images
Use these validators:
Architecture Patterns
Static Site Pattern
Build Time:
Content → Generate OG Images → Save to /public/og/
Runtime:
Page → Reference /og/page-slug.pngDynamic API Pattern
Request:
/api/og?title=Hello&subtitle=World
↓
Check Cache → Hit? Return cached
↓
Generate → Save to cache → ReturnSigned URL Pattern
Build/SSR:
Get signed URL from API (with API key)
↓
Store signed URL in meta tags
Client:
Browser requests signed URL directly
No API key exposedChecklist
- [ ] API key stored securely in environment variables
- [ ] Input validation on all user-provided content
- [ ] Fallback images for error cases
- [ ] Caching implemented (CDN, Redis, or file system)
- [ ] Rate limiting handled with backoff
- [ ] Complete meta tags including dimensions
- [ ] Absolute URLs in all meta tags
- [ ] Tested on Facebook, Twitter, LinkedIn validators