6.9 KiB
R2 Custom Domain Setup Guide
This guide walks through setting up a production-ready custom domain for the Sojorn R2 bucket.
Why Custom Domain?
The R2 public development URL (https://pub-*.r2.dev) has significant limitations:
- ⚠️ Rate limited - not suitable for production traffic
- ⚠️ No Cloudflare features (Access, Caching, Analytics)
- ⚠️ No custom SSL certificates
- ⚠️ Not recommended by Cloudflare for production
A custom domain provides:
- ✅ Unlimited bandwidth and requests
- ✅ Full Cloudflare features (caching, CDN, DDoS protection)
- ✅ Custom SSL certificates
- ✅ Professional appearance
- ✅ Better SEO and branding
Recommended Domain Structure
If your main domain is sojorn.com, use a subdomain for media:
media.sojorn.com- Professional, clear purposecdn.sojorn.com- Common CDN patternimages.sojorn.com- Descriptive alternative
Recommended: media.sojorn.com
Setup Steps
Step 1: Connect Domain to R2 Bucket
- Go to Cloudflare Dashboard → R2 →
sojorn-mediabucket - Click "Settings" tab
- Under "Public Access", click "Connect Domain"
- Enter your chosen subdomain:
media.sojorn.com - Click "Connect Domain"
Cloudflare will automatically:
- Create a DNS CNAME record pointing to R2
- Provision an SSL certificate
- Enable CDN caching
Step 2: Verify Domain Configuration
Wait 1-2 minutes for DNS propagation, then test:
# Check DNS record
nslookup media.sojorn.com
# Test direct access (upload a test file first)
curl -I https://media.sojorn.com/test-image.jpg
# Should return: HTTP/2 200
Step 3: Configure Environment Variable
Set the public URL in Supabase secrets:
# Set the R2 public URL secret
npx supabase secrets set R2_PUBLIC_URL=https://media.sojorn.com --project-ref zwkihedetedlatyvplyz
# Verify all R2 secrets are set
npx supabase secrets list --project-ref zwkihedetedlatyvplyz
Expected secrets:
R2_ACCOUNT_ID- Your Cloudflare account IDR2_ACCESS_KEY- R2 API token access keyR2_SECRET_KEY- R2 API token secret keyR2_PUBLIC_URL- Your custom domain URL (e.g., https://media.sojorn.com)
Step 4: Deploy Updated Edge Function
The edge function now uses the R2_PUBLIC_URL environment variable:
# Deploy the updated edge function
npx supabase functions deploy upload-image --project-ref zwkihedetedlatyvplyz
# Verify deployment
npx supabase functions list --project-ref zwkihedetedlatyvplyz
Step 5: Test End-to-End
- Upload a test image through the app
- Check the database for the generated URL:
SELECT id, image_url, created_at FROM posts WHERE image_url IS NOT NULL ORDER BY created_at DESC LIMIT 1; - Verify URL format: Should be
https://media.sojorn.com/{uuid}.{ext} - Test in browser: Open the URL directly - image should load
- Check in app: Image should display in the feed
Troubleshooting
Domain Not Connecting
Error: "Failed to connect domain" Solution:
- Verify domain is managed by Cloudflare (same account)
- Check domain isn't already connected to another R2 bucket
- Ensure subdomain doesn't have conflicting DNS records
SSL Certificate Issues
Error: "SSL handshake failed" or "NET::ERR_CERT_COMMON_NAME_INVALID" Solution:
- Wait 5-10 minutes for SSL certificate provisioning
- Verify domain shows "Active" status in R2 bucket settings
- Check Cloudflare SSL/TLS mode is set to "Full" or "Full (strict)"
Images Return 404
Error: Images uploaded but return 404 on custom domain Solution:
- Verify domain connection is "Active" in R2 settings
- Check file actually exists:
curl -I https://{ACCOUNT_ID}.r2.cloudflarestorage.com/sojorn-media/{filename} - Verify bucket name matches in edge function (should be
sojorn-media)
Old Dev URLs Still Used
Problem: New uploads use dev URL instead of custom domain Solution:
- Verify
R2_PUBLIC_URLsecret is set:npx supabase secrets list - Redeploy edge function:
npx supabase functions deploy upload-image - Check edge function logs for errors:
npx supabase functions logs upload-image
Cloudflare Caching Configuration
After connecting the domain, optimize caching in Cloudflare Dashboard:
- Go to your domain in Cloudflare Dashboard
- Navigate to Rules → Page Rules
- Create a rule for
media.sojorn.com/*:- Cache Level: Standard
- Edge Cache TTL: 1 month
- Browser Cache TTL: 1 hour
This ensures images are cached at Cloudflare's edge for fast global delivery.
Security Considerations
CORS Configuration
The R2 bucket CORS is already configured for all origins (*):
{
"Allowed Origins": "*",
"Allowed Methods": ["GET", "HEAD", "PUT"],
"Allowed Headers": "*"
}
For production, consider restricting origins:
{
"Allowed Origins": ["https://sojorn.com", "https://app.sojorn.com"],
"Allowed Methods": ["GET", "HEAD"],
"Allowed Headers": ["*"]
}
Access Control
Images are publicly readable by design. To restrict access:
- Use signed URLs (requires code changes)
- Implement Cloudflare Access rules
- Add authentication checks before generating upload URLs
Cost Considerations
R2 Pricing (as of 2026)
- Storage: $0.015/GB per month
- Class A Operations (writes): $4.50 per million requests
- Class B Operations (reads): $0.36 per million requests
- Data Transfer: FREE (no egress fees)
Custom Domain Benefits
- Cloudflare CDN: Free caching reduces R2 read operations
- No Egress Fees: Unlike AWS S3, R2 doesn't charge for bandwidth
- Edge Caching: Reduces origin requests by 95%+
Monitoring
Track R2 usage in Cloudflare Dashboard:
- Go to R2 →
sojorn-mediabucket - Check Metrics tab for:
- Storage size
- Request count
- Bandwidth usage
Set up alerts for:
- Storage exceeding threshold (e.g., 10GB)
- Unusual request spikes
- Error rate increases
Quick Reference
Required Supabase Secrets
R2_ACCOUNT_ID=<your-cloudflare-account-id>
R2_ACCESS_KEY=<your-r2-access-key>
R2_SECRET_KEY=<your-r2-secret-key>
R2_PUBLIC_URL=https://media.sojorn.com
Deploy Commands
# Set secret
npx supabase secrets set R2_PUBLIC_URL=https://media.sojorn.com --project-ref zwkihedetedlatyvplyz
# Deploy function
npx supabase functions deploy upload-image --project-ref zwkihedetedlatyvplyz
# View logs
npx supabase functions logs upload-image --project-ref zwkihedetedlatyvplyz
Test Commands
# Check DNS
nslookup media.sojorn.com
# Test HTTPS
curl -I https://media.sojorn.com/
# Upload test file
curl -X PUT "https://<account-id>.r2.cloudflarestorage.com/sojorn-media/test.txt" \
-H "Authorization: Bearer <token>" \
--data "test content"
# Verify via custom domain
curl -I https://media.sojorn.com/test.txt
Next Steps: After completing this setup, proceed to the main IMAGE_UPLOAD_IMPLEMENTATION.md guide for testing the full upload flow.