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-Control headers 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/og for 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

ProviderEgress costGlobal CDNDynamic transformsBest for
S3 + CloudFront$0.085/GB✓ ExcellentNoAWS teams
GCS + CDN$0.08/GB✓ GoodNoGCP/Firebase
CloudinaryFree tier + paid✓ Excellent✓ Best-in-classNo-code dynamic OG
Vercel CDNIncluded in plan✓ Good✓ Via next/ogNext.js / Vercel
Cloudflare R2$0 egress✓ ExcellentNoCost-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.

Related Guides