OG Image Hosting Comparison: S3, GCS, Cloudinary, and Vercel CDN
Compare AWS S3, Google Cloud Storage, Cloudinary, and Vercel CDN for hosting og:images — cost, cache-control, HTTPS, global latency, and ease of use.
What matters when hosting OG images
Your og:image URL gets fetched by social crawlers — Twitterbot, LinkedInBot, Discordbot, and Slackbot — every time someone shares your link. These crawlers have requirements:
- The image must be served over HTTPS.
- The URL must be publicly accessible (no auth, no signed URLs that expire).
- Response time matters — slow images may not be loaded before the crawler times out.
Cache-Controlheaders determine how long platforms cache your image. Immutable images should use long TTLs.- Size: keep OG images under 5 MB (ideally under 1 MB for fast loading).
AWS S3 + CloudFront
S3 is the most common choice for teams already on AWS. Raw S3 URLs (s3.amazonaws.com) are slow for global users — always pair S3 with CloudFront CDN for OG images.
# Upload with correct headers aws s3 cp ./og/my-post.png s3://your-bucket/og/my-post.png \ --content-type image/png \ --cache-control "public, max-age=31536000, immutable" \ --acl public-read # Your OG image URL (via CloudFront): # https://dXXXXXXXXXXXX.cloudfront.net/og/my-post.png
- Cost: ~$0.023/GB storage + $0.085/GB egress (CloudFront cheaper at scale)
- HTTPS: Yes (CloudFront provides TLS, or use your own domain)
- Latency: Excellent globally via 400+ CloudFront edge locations
- Custom domain: Yes, via CloudFront alternate domain names
- Cache busting: Change the filename or use CloudFront invalidations ($0.005/path)
- Best for: Teams on AWS, large image volumes, fine-grained cache control
Google Cloud Storage + Cloud CDN
GCS is S3's equivalent on Google Cloud. Enable Uniform bucket-level accessand make objects public, then front with Cloud CDN or Firebase Hosting for global edge caching.
# Upload to GCS
gsutil -h "Content-Type:image/png" \
-h "Cache-Control:public, max-age=31536000, immutable" \
cp ./og/my-post.png gs://your-bucket/og/my-post.png
# Make public
gsutil acl ch -u AllUsers:R gs://your-bucket/og/my-post.png
# Default GCS URL:
# https://storage.googleapis.com/your-bucket/og/my-post.png- Cost: ~$0.020/GB storage + $0.08/GB egress (first 1 TB/month free)
- HTTPS: Yes, default storage.googleapis.com URL is HTTPS
- Latency: Good globally; better with Cloud CDN enabled
- Custom domain: Requires Cloud CDN or Firebase Hosting
- Best for: Teams on GCP, Firebase projects
Cloudinary
Cloudinary is a media-focused CDN with built-in image transformation via URL parameters. You can generate OG images dynamically using Cloudinary's URL-based transformation API — no code needed for simple overlays.
# Upload base template
cloudinary.uploader.upload('./og-template.png', {
public_id: 'og/template',
resource_type: 'image',
});
# Generate OG image URL with text overlay:
https://res.cloudinary.com/your-cloud/image/upload/
w_1200,h_630,c_fill/
l_text:Arial_52_bold:My%20Post%20Title,co_white,g_south_west,x_64,y_64/
og/template.png- Cost: Free tier: 25 GB storage + 25 GB bandwidth/month; paid from $89/month
- HTTPS: Yes, always
- Latency: Excellent — global CDN with automatic format optimization
- Dynamic images: Best-in-class URL-based transforms; no code server needed
- Best for: Teams wanting dynamic OG image generation without a compute layer
Vercel CDN (via Next.js or static deploy)
If you're already on Vercel, the simplest option is to put OG images in your public/og/ folder and reference them as absolute URLs. Vercel automatically serves public/ via their global CDN with HTTP/2 and proper cache headers.
// Place image at: public/og/my-post.png
// Reference as: https://yourdomain.com/og/my-post.png
// In Next.js metadata:
export const metadata = {
openGraph: {
images: [{
url: 'https://yourdomain.com/og/my-post.png',
width: 1200,
height: 630,
}],
},
};- Cost: Included in Vercel plan; Pro plan has 1 TB bandwidth/month
- HTTPS: Yes, always
- Latency: Good globally (Vercel Edge Network)
- Dynamic images: Use
next/ogfor on-demand generation - Best for: Next.js teams, simple static OG images, zero additional infrastructure
Cloudflare R2 (honorable mention)
Cloudflare R2 is an S3-compatible object store with zero egress fees — unlike S3 and GCS which charge per GB transferred. Pair with Cloudflare's global CDN for near-zero cost OG image hosting at scale.
# R2 public bucket URL format: # https://pub-XXXXXXXXXXXX.r2.dev/og/my-post.png # Or custom domain via Cloudflare DNS
R2 is the best cost option at volume — no egress charges make it especially attractive for high-traffic sites.
Quick comparison summary
| Provider | Egress cost | Global CDN | Dynamic transforms | Best for |
|---|---|---|---|---|
| S3 + CloudFront | $0.085/GB | ✓ Excellent | No | AWS teams |
| GCS + CDN | $0.08/GB | ✓ Good | No | GCP/Firebase |
| Cloudinary | Free tier + paid | ✓ Excellent | ✓ Best-in-class | No-code dynamic OG |
| Vercel CDN | Included in plan | ✓ Good | ✓ Via next/og | Next.js / Vercel |
| Cloudflare R2 | $0 egress | ✓ Excellent | No | Cost-sensitive / high volume |
Test your OG tags free
Paste any URL into OGFixer to see exactly how your link previews look on Twitter, LinkedIn, Discord, and Slack.