Client Area

Cloudflare Workers and R2 Storage for DomainIndia Apps

ByDomain India Team·DomainIndia Engineering
6 min read24 Apr 20263 views
# Cloudflare Workers and R2 Storage for DomainIndia Apps
TL;DR
Cloudflare Workers run your JavaScript/TypeScript at 330+ edge locations — sub-50ms globally. Paired with R2 (zero-egress S3-compatible storage), you can offload images, static APIs, and auth-edge checks from your DomainIndia origin. This guide covers when to use Workers, a working example, and R2 integration.
## When Workers make sense Workers aren't a replacement for your DomainIndia hosting — they complement it. Good use cases: - **Edge auth** — validate JWT at the edge; reject unauthenticated requests before they hit your origin - **A/B testing** — route 10% of traffic to a different backend without touching your app - **Image resize / optimise** — serve correctly-sized images on-demand - **Geolocation redirects** — send India traffic to one origin, EU to another - **Cache tiering** — cache expensive API responses at edge - **Webhook receivers** — terminate third-party webhooks, forward compacted data to origin Bad use cases: full apps with complex state, heavy CPU work, long-running jobs. For those, stick with your DomainIndia VPS. ## Free-tier limits (2026) - 100,000 requests/day per worker (free plan) - 10 ms CPU time per request - 128 MB memory - 1 MB request/response size Paid plan ($5/mo): 10M requests/month, 50 ms CPU, no artificial request limits. R2: 10 GB storage + 1M Class A ops + 10M Class B ops free forever. **Zero egress fees** — this is R2's killer feature vs S3. ## Step 1 — Install Wrangler Wrangler is Cloudflare's CLI for Workers. ```bash npm install -g wrangler wrangler login # opens browser, auth with your Cloudflare account ``` ## Step 2 — Create a Worker ```bash mkdir edge-auth && cd edge-auth wrangler init --yes --type=javascript ``` `src/index.js`: ```javascript export default { async fetch(request, env, ctx) { // 1. Edge auth — check JWT before hitting origin const auth = request.headers.get('Authorization'); if (!auth?.startsWith('Bearer ')) { return new Response('Unauthorized', { status: 401 }); } const token = auth.slice(7); const valid = await verifyJWT(token, env.JWT_SECRET); if (!valid) return new Response('Invalid token', { status: 401 }); // 2. Forward to DomainIndia origin const url = new URL(request.url); url.hostname = 'origin.yourcompany.com'; // your DomainIndia VPS domain const originRequest = new Request(url, request); const response = await fetch(originRequest); // 3. Add edge headers const modified = new Response(response.body, response); modified.headers.set('X-Auth-At-Edge', 'true'); modified.headers.set('X-User-Id', valid.userId); return modified; }, }; async function verifyJWT(token, secret) { const [h, p, s] = token.split('.'); if (!h || !p || !s) return null; const msg = `${h}.${p}`; const key = await crypto.subtle.importKey( 'raw', new TextEncoder().encode(secret), { name: 'HMAC', hash: 'SHA-256' }, false, ['verify'] ); const sig = Uint8Array.from( atob(s.replace(/-/g, '+').replace(/_/g, '/')), c => c.charCodeAt(0) ); const ok = await crypto.subtle.verify( 'HMAC', key, sig, new TextEncoder().encode(msg) ); if (!ok) return null; return JSON.parse(atob(p)); } ``` `wrangler.toml`: ```toml name = "edge-auth" main = "src/index.js" compatibility_date = "2026-04-01" [vars] # non-secret config here [env.production] routes = [ { pattern = "api.yourcompany.com/*", zone_name = "yourcompany.com" } ] ``` Set secret: ```bash wrangler secret put JWT_SECRET # paste secret, press Ctrl+D ``` Deploy: ```bash wrangler deploy ``` Within 30 seconds, `api.yourcompany.com` is served through the Worker globally. ## Step 3 — R2 object storage R2 is S3-compatible but with zero egress cost. Perfect for user uploads, backups, static assets. Create a bucket: ```bash wrangler r2 bucket create uploads ``` In your Worker, bind the bucket in `wrangler.toml`: ```toml [[r2_buckets]] binding = "UPLOADS" bucket_name = "uploads" ``` Access from Worker: ```javascript export default { async fetch(request, env) { const url = new URL(request.url); if (request.method === 'PUT') { // Upload const key = url.pathname.slice(1); // "user-123/photo.jpg" await env.UPLOADS.put(key, request.body, { httpMetadata: { contentType: request.headers.get('Content-Type') }, }); return new Response('Uploaded', { status: 201 }); } if (request.method === 'GET') { const key = url.pathname.slice(1); const object = await env.UPLOADS.get(key); if (!object) return new Response('Not found', { status: 404 }); return new Response(object.body, { headers: { 'Content-Type': object.httpMetadata.contentType }, }); } return new Response('Method not allowed', { status: 405 }); }, }; ``` ## Connecting R2 from your DomainIndia app Your DomainIndia VPS/shared app can upload directly to R2 using any S3 SDK. Credentials: Cloudflare Dashboard → R2 → Manage R2 API Tokens. Node.js example (works identically from any hosting): ```javascript import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3'; const s3 = new S3Client({ region: 'auto', endpoint: `https://${ACCOUNT_ID}.r2.cloudflarestorage.com`, credentials: { accessKeyId: process.env.R2_ACCESS_KEY_ID, secretAccessKey: process.env.R2_SECRET_ACCESS_KEY, }, }); await s3.send(new PutObjectCommand({ Bucket: 'uploads', Key: 'photo.jpg', Body: fileBuffer, ContentType: 'image/jpeg', })); ``` ## Cost comparison — R2 vs S3 vs DomainIndia bandwidth For an app serving 100 GB/month of images:
StorageCost (₹/mo @ 100 GB)Egress (₹/mo @ 100 GB out)
R2 (Cloudflare)₹125₹0 (free egress)
AWS S3₹210₹700 (first GB free, then $0.09/GB)
Backblaze B2₹50₹85 (3× stored free)
DomainIndia bandwidthIncluded in planCounts toward your plan quota
R2 wins for public-facing content. DomainIndia bandwidth is fine for low-traffic apps or internal use. B2 wins for pure backup storage with minimal downloads. ## Durable Objects — stateful edge (advanced) For chat apps, live collaboration, rate limiting by user: Durable Objects give you a single logical instance of state at the edge, serialised. ```javascript export class RateLimiter { constructor(state, env) { this.state = state; } async fetch(request) { const count = (await this.state.storage.get('count')) ?? 0; await this.state.storage.put('count', count + 1); return new Response(JSON.stringify({ count })); } } // Route to the DO in your Worker: const id = env.RATE_LIMITER.idFromName(userId); const obj = env.RATE_LIMITER.get(id); return obj.fetch(request); ``` Each user has their own DO instance, automatically deployed near them. ## Common pitfalls ## FAQ
Q Does Workers replace my DomainIndia hosting?

No. Workers are for edge logic — small, fast, stateless. Your app, database, and long-running processes stay on DomainIndia.

Q Can Workers talk to my PostgreSQL on DomainIndia?

Not directly — Workers use fetch() only. Use a REST/GraphQL API on your VPS, or Cloudflare Hyperdrive (beta) which pools connections.

Q Is R2 reliable enough for production?

Yes. 99.9% SLA, 11 nines durability. Used by major SaaS companies. Pair with lifecycle rules for automatic cold-storage migration.

Q Can I serve a full website from Workers?

Cloudflare Pages is better for static sites + Functions. Workers are more for API edge logic. Use DomainIndia for your main app, Workers for the thin edge layer.

Q How do I debug Workers in production?

wrangler tail --format json streams logs live. Add to Logpush for long-term retention in R2.

Use Cloudflare Workers in front of your DomainIndia hosting for global edge speed. See hosting plans

Was this article helpful?

Your feedback helps us improve our documentation

Still need help? Submit a support ticket