Skip to content

This guide walks you through setting up a custom CDN domain for your Cloudflare R2 bucket to serve uploaded images efficiently.

Prerequisites

  • Cloudflare account with R2 enabled
  • Domain managed by Cloudflare (or partial CNAME setup)
  • R2 bucket already created (e.g., tendsocial)

Step 1: Navigate to R2 Bucket Settings

  1. Log in to your Cloudflare Dashboard
  2. Go to R2 in the left sidebar
  3. Select your bucket (e.g., tendsocial)
  4. Click on the Settings tab

Step 2: Connect Custom Domain

  1. Under Public access or Custom Domains, click Connect Domain
  2. Enter your desired subdomain (e.g., cdn.tendsocial.com)
  3. Click Continue

Cloudflare will automatically:

  • Create a CNAME DNS record pointing to your R2 bucket
  • Provision an SSL certificate
  • Enable CDN caching

Step 3: Verify Domain Status

  1. Wait for the domain status to change from "Initializing" to "Active" (usually 1-2 minutes)
  2. Refresh the page if needed
  3. Once active, your custom domain is ready to use

To optimize CDN performance:

  1. Go to CacheCache Rules in your Cloudflare dashboard
  2. Create a new rule:
    • Name: R2 Image Caching
    • When incoming requests match: Hostname equals cdn.tendsocial.com
    • Then:
      • Cache eligibility: Eligible for cache
      • Cache TTL: 1 year (or custom)
      • Browser TTL: 1 year
  3. Save and deploy

Step 5: Configure CORS (Required for Presigned URLs)

For client-side uploads using presigned URLs, you need to configure CORS:

  1. In your R2 bucket settings, find CORS Policy
  2. Add the following configuration:
json
[
  {
    "AllowedOrigins": [
      "https://app.tendsocial.com",
      "http://localhost:5173"
    ],
    "AllowedMethods": [
      "GET",
      "PUT",
      "POST",
      "DELETE",
      "HEAD"
    ],
    "AllowedHeaders": [
      "*"
    ],
    "ExposeHeaders": [
      "ETag"
    ],
    "MaxAgeSeconds": 3600
  }
]

Note: Update AllowedOrigins to match your actual frontend domains.

Step 6: Update Environment Variables

Update your backend environment variables:

bash
# Cloudflare R2 Configuration
S3_BUCKET=tendsocial
S3_REGION=auto
S3_ENDPOINT=https://370e3c7ed6e50972fea1a19da4eb21c6.r2.cloudflarestorage.com
AWS_ACCESS_KEY_ID=your-r2-access-key-id
AWS_SECRET_ACCESS_KEY=your-r2-secret-access-key

# CDN Domain (custom domain you just configured)
CDN_DOMAIN=cdn.tendsocial.com

For Google Cloud Run:

  1. Go to your Cloud Run service
  2. Edit & Deploy New Revision
  3. Add/update the CDN_DOMAIN secret/environment variable
  4. Deploy

Step 7: Test the Setup

Test 1: Upload an Image

bash
# Generate presigned URL
curl -X POST https://api.tendsocial.com/api/upload/presigned-url \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "filename": "test.png",
    "contentType": "image/png",
    "fileSize": 50000
  }'

You should receive a response with:

  • uploadUrl - Presigned URL for upload
  • cdnUrl - CDN URL (e.g., https://cdn.tendsocial.com/...)
  • key - S3 object key
  • imageId - Database record ID

Test 2: Upload to Presigned URL

bash
curl -X PUT "PRESIGNED_URL_FROM_ABOVE" \
  -H "Content-Type: image/png" \
  --data-binary "@path/to/test.png"

Test 3: Access via CDN

bash
curl -I "https://cdn.tendsocial.com/YOUR_FILE_KEY"

Expected response:

  • Status: 200 OK
  • cf-cache-status: HIT (after first request)
  • cache-control: public, max-age=31536000, immutable

Troubleshooting

Domain Status Stuck on "Initializing"

  • Wait 5-10 minutes
  • Ensure your domain is properly configured in Cloudflare DNS
  • Check that there are no conflicting DNS records

CORS Errors in Browser

  • Verify CORS policy is correctly configured in R2 bucket settings
  • Ensure your frontend domain is in the AllowedOrigins list
  • Check browser console for specific CORS error messages

Images Not Loading

  • Verify the CDN domain is active
  • Check that CDN_DOMAIN environment variable is set correctly
  • Test direct R2 URL first (without CDN) to isolate the issue

Cache Not Working

  • Verify cache rules are configured
  • Check cache-control headers in response
  • Use curl -I to inspect headers
  • Clear Cloudflare cache if needed (Caching → Configuration → Purge Everything)

Performance Optimization

Enable Smart Tiered Cache

  1. Go to CachingTiered Cache
  2. Enable Smart Tiered Cache
  3. This reduces requests to R2 origin and improves global performance

Enable Argo Smart Routing (Optional, Paid)

For even faster global delivery:

  1. Go to TrafficArgo
  2. Enable Argo Smart Routing
  3. This optimizes routing for your CDN traffic

Security Best Practices

  1. Use Presigned URLs for Uploads: Never expose R2 credentials to the client
  2. Validate File Types: Always validate on the server before generating presigned URLs
  3. Set Expiration Times: Keep presigned URL expiration short (1 hour recommended)
  4. Monitor Usage: Set up alerts for unusual upload patterns
  5. Rate Limiting: Implement rate limits on upload endpoints (already configured in API)

Cost Considerations

  • R2 Storage: $0.015/GB/month
  • Class A Operations (writes): $4.50 per million requests
  • Class B Operations (reads): $0.36 per million requests
  • Egress: FREE when using Cloudflare CDN (custom domain)

Important: Using a custom domain (CDN) eliminates egress fees. Direct R2 URLs (.r2.dev) have egress charges.

Next Steps

  • [ ] Set up monitoring for R2 usage in Cloudflare dashboard
  • [ ] Configure alerts for storage limits
  • [ ] Implement image optimization (resizing, compression) if needed
  • [ ] Set up automated cleanup for orphaned files

References

TendSocial Documentation