1<!DOCTYPE html><html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="preload" as="image" href="/CF-Logo 1.png"/>
1<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async="" defer=""></script>
1<title>R2 Local Uploads Demo</title><meta name="description" content="See the performance benefits of R2 Local Uploads"/><link rel="icon" type="image/png" href="/favicon.png"/><link rel="preconnect" href="https://fonts.googleapis.com"/><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="anonymous"/><link rel="modulepreload" href="/assets/entry.client-BxWvKjUH.js"/><link rel="modulepreload" href="/assets/chunk-JMJ3UQ3L-O5Vcyypg.js"/><link rel="modulepreload" href="/assets/root-CTov3YhH.js"/><link rel="modulepreload" href="/assets/home-Dr2oDBhj.js"/><link rel="stylesheet" href="/assets/root-Zspsc9VA.css"/><link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap"/></head><body><main class="h-screen flex flex-col bg-white text-gray-900 overflow-hidden"><header class="flex-none flex items-center justify-between px-4 py-3 lg:px-6 lg:py-4 bg-white border-b border-gray-200"><div class="flex items-center gap-3 lg:gap-4"><a href="https://workers.cloudflare.com/product/r2/" target="_blank" rel="noopener noreferrer"><img src="/CF-Logo 1.png" alt="Cloudflare" class="h-5 lg:h-6 w-auto"/></a><span class="text-gray-300">|</span><span class="text-xs lg:text-sm font-medium text-gray-600">Cloudflare R2 Local Uploads</span></div><a href="https://developers.cloudflare.com/r2/buckets/local-uploads/" target="_blank" rel="noopener noreferrer" class="text-xs lg:text-sm text-gray-500 hover:text-orange-500 flex items-center gap-1">Docs<svg class="w-3 lg:w-3.5 h-3 lg:h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"></path></svg></a></header><div class="flex-1 flex flex-col lg:flex-row min-h-0"><div class="flex-1 flex flex-col min-h-0 order-1 lg:order-2 h-[45vh] lg:h-auto"><div class="flex-1 relative overflow-hidden bg-white"><div class="absolute inset-0 pointer-events-none" style="background-image:radial-gradient(circle, rgba(243, 128, 32, 0.25) 1.5px, transparent 1.5px), 2 radial-gradient(circle, rgba(243, 128, 32, 0.18) 1px, transparent 1px), 3 radial-gradient(circle, rgba(243, 128, 32, 0.1) 0.5px, transparent 0.5px);background-size:80px 80px, 40px 40px, 20px 20px;background-position:0 0, 20px 20px, 10px 10px"></div><div class="absolute inset-0 flex items-center justify-center overflow-hidden"><div id="globe-container" style="width:230%;height:230%"></div><div class="absolute inset-0 flex items-center justify-center bg-white"><div class="flex flex-col items-center gap-3"><div class="w-8 h-8 border-2 border-orange-200 border-t-orange-500 rounded-full animate-spin"></div><span class="text-gray-400 text-sm">Loading globe...</span></div></div></div><div class="absolute top-2 right-2 lg:top-4 lg:right-4 bg-white/90 backdrop-blur-sm rounded px-2 py-1.5 lg:px-3 lg:py-2 shadow-sm border border-gray-100"><div class="flex flex-row lg:flex-col gap-2 lg:gap-1.5 text-[9px] lg:text-xs text-gray-600"><div class="flex items-center gap-1.5 lg:gap-2"><div class="flex flex-col gap-1 flex-shrink-0"><div class="w-4 lg:w-5 h-0.5 bg-orange-500 rounded-full"></div><div class="w-4 lg:w-5 h-0.5 bg-gray-400 rounded-full"></div></div><span class="whitespace-nowrap">Direct</span></div><div class="flex items-center gap-1.5 lg:gap-2"><div class="w-4 lg:w-5 h-0.5 rounded-full flex-shrink-0" style="background:repeating-linear-gradient(90deg, #F8AD4C 0, #F8AD4C 2px, transparent 2px, transparent 4px)"></div><span class="whitespace-nowrap">Async replication</span></div></div></div></div></div><div class="order-3 lg:order-1 w-full lg:w-80 xl:w-96 flex-none flex flex-col p-4 lg:p-6 border-t lg:border-t-0 lg:border-r border-gray-200 bg-[#fafafa]" style="background-image:radial-gradient(circle, rgba(0, 0, 0, 0.08) 1px, transparent 1px);background-size:16px 16px"><div class="mb-4 lg:mb-6"><div class="group relative inline-block"><p class="text-xs font-bold tracking-[0.3em] text-orange-500 mb-1 lg:mb-2 flex items-center gap-2"><span class="text-[9px] font-semibold tracking-normal px-1.5 py-0.5 bg-orange-100 text-orange-600 rounded">BETA</span>LOCAL UPLOADS<button class="text-gray-400 hover:text-orange-400 transition-colors">â</button></p><div class="absolute left-0 top-full mt-1 px-3 py-2 bg-gray-900 text-white text-xs rounded-lg transition-opacity pointer-events-auto w-72 z-50 opacity-0 lg:group-hover:opacity-100 pointer-events-none lg:pointer-events-auto">Local Uploads writes object data to a nearby storage then asynchronously copies it to your bucket â all while data is available immediately. <a href="https://developers.cloudflare.com/r2/buckets/local-uploads/" target="_blank" rel="noopener noreferrer" class="text-orange-300 hover:text-orange-200 underline">Learn more</a>.<div class="absolute bottom-full right-20 border-4 border-transparent border-b-gray-900"></div></div></div><p class="text-sm lg:text-lg text-gray-700">Improve cross-region upload performance in R2 with Local Uploads.</p></div><div class="flex items-center gap-2 text-[11px] lg:text-sm text-gray-500 mb-3 lg:mb-6 pb-3 lg:pb-6 border-b border-gray-200"><svg class="w-4 h-4 flex-shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z"></path><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 11a3 3 0 11-6 0 3 3 0 016 0z"></path></svg><span class="text-gray-400">Detecting location...</span></div><div class="space-y-2 lg:space-y-4 mb-3 lg:mb-6"><div class="flex flex-row lg:flex-col gap-2 lg:gap-4"><div class="flex-1"><div class="flex items-center gap-1.5 mb-2"><label class="text-[10px] lg:text-xs font-medium text-gray-500">FILE SIZE</label><div class="relative"><button class="hover:text-gray-600"><svg class="w-3.5 h-3.5 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg></button></div></div><select class="lg:hidden cf-select w-full px-2 py-2 text-xs rounded-md border border-gray-200 bg-white text-gray-900 focus:outline-none focus:border-orange-500 cursor-pointer"><option value="large">50 MB</option><option value="medium" selected="">5 MB</option><option value="small">100 KB</option></select><div class="hidden lg:flex relative gap-2 bg-white rounded-md border border-gray-200"><div class="absolute top-0 bottom-0 bg-orange-100 rounded-md transition-all duration-200 ease-out" style="width:calc(100% / 3);left:calc(1 * 100% / 3)"></div><button class="relative z-10 flex-1 py-2 px-3 text-center rounded-md transition-colors duration-200 text-sm text-gray-600 hover:text-gray-900">50 MB</button><button class="relative z-10 flex-1 py-2 px-3 text-center rounded-md transition-colors duration-200 text-sm text-orange-600 font-medium">5 MB</button><button class="relative z-10 flex-1 py-2 px-3 text-center rounded-md transition-colors duration-200 text-sm text-gray-600 hover:text-gray-900">100 KB</button></div></div><div class="flex-1"><div class="flex items-center gap-1.5 mb-2"><label class="text-[10px] lg:text-xs font-medium text-gray-500">BUCKET REGION</label><div class="relative"><button class="hover:text-gray-600"><svg class="w-3.5 h-3.5 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg></button></div></div><select class="cf-select w-full px-2 lg:px-3 py-2 text-xs lg:text-sm rounded-md border border-gray-200 bg-white text-gray-900 focus:outline-none focus:border-orange-500 cursor-pointer"><option value="wnam">Western North America</option><option value="enam">Eastern North America</option><option value="weur">Western Europe</option><option value="eeur" selected="">Eastern Europe</option><option value="apac">Asia Pacific</option><option value="oc">Oceania</option></select></div></div><div class="pt-2 pb-2 lg:pt-0 lg:pb-0"><button disabled="" class="cf-button-primary w-full !text-sm lg:!text-base font-normal whitespace-nowrap !py-2 lg:!py-3">Complete verification below</button></div><div class="flex flex-col items-center gap-1 lg:gap-2"><div class="scale-75 lg:scale-100 origin-center -my-2 lg:my-0"></div></div></div><div class="mt-auto pt-4 border-t border-gray-200"><p class="text-[8px] lg:text-[10px] text-gray-400 leading-relaxed">
3This demo illustrates the relative benefit of Local Uploads. Uploads are performed server-side from a Cloudflare Worker close to you. <em>Results may vary depending on network conditions and server load.</em></p></div></div></div></main>
3<script>((storageKey2, restoreKey) => { 4 if (!window.history.state || !window.history.state.key) { 5 let key = Math.random().toString(32).slice(2); 6 window.history.replaceState({ key }, ""); 7 } 8 try { 9 let positions = JSON.parse(sessionStorage.getItem(storageKey2) || "{}"); 10 let storedY = positions[restoreKey || window.history.state.key]; 11 if (typeof storedY === "number") { 12 window.scrollTo(0, storedY); 13 } 14 } catch (error) { 15 console.error(error); 16 sessionStorage.removeItem(storageKey2); 17 } 18 })("react-router-scroll-positions", null)</script>
18<script>window.__reactRouterContext = {"basename":"/","future":{"unstable_optimizeDeps":false,"unstable_subResourceIntegrity":false,"v8_middleware":false,"v8_splitRouteModules":false,"v8_viteEnvironmentApi":true},"routeDiscovery":{"mode":"lazy","manifestPath":"/__manifest"},"ssr":true,"isSpaMode":false};window.__reactRouterContext.stream = new ReadableStream({start(controller){window.__reactRouterContext.streamController = controller;}}).pipeThrough(new TextEncoderStream());</script>
18<script type="module" async="">; 19import * as route0 from "/assets/root-CTov3YhH.js"; 20import * as route1 from "/assets/home-Dr2oDBhj.js"; 21 window.__reactRouterManifest = { 22 "entry": { 23 "module": "/assets/entry.client-BxWvKjUH.js", 24 "imports": [ 25 "/assets/chunk-JMJ3UQ3L-O5Vcyypg.js" 26 ], 27 "css": [] 28 }, 29 "routes": { 30 "root": { 31 "id": "root", 32 "path": "", 33 "hasAction": false, 34 "hasLoader": false, 35 "hasClientAction": false, 36 "hasClientLoader": false, 37 "hasClientMiddleware": false, 38 "hasErrorBoundary": true, 39 "module": "/assets/root-CTov3YhH.js", 40 "imports": [ 41 "/assets/chunk-JMJ3UQ3L-O5Vcyypg.js" 42 ], 43 "css": [ 44 "/assets/root-Zspsc9VA.css" 45 ] 46 }, 47 "routes/home": { 48 "id": "routes/home", 49 "parentId": "root", 50 "index": true, 51 "hasAction": false, 52 "hasLoader": true, 53 "hasClientAction": false, 54 "hasClientLoader": false, 55 "hasClientMiddleware": false, 56 "hasErrorBoundary": false, 57 "module": "/assets/home-Dr2oDBhj.js", 58 "imports": [ 59 "/assets/chunk-JMJ3UQ3L-O5Vcyypg.js" 60 ], 61 "css": [] 62 } 63 }, 64 "url": "/assets/manifest-e0c67d17.js", 65 "version": "e0c67d17" 66}; 67 window.__reactRouterRouteModules = {"root":route0,"routes/home":route1}; 68 69import("/assets/entry.client-BxWvKjUH.js");</script>
69<!--$-->
69<script>window.__reactRouterContext.streamController.enqueue("[{\"_1\":2,\"_7\":-5,\"_8\":-5},\"loaderData\",{\"_3\":4},\"routes/home\",{\"_5\":6},\"turnstileSiteKey\",\"0x4AAAAAACUT3pff4fSNupbv\",\"actionData\",\"errors\"]\n");</script>
69<!--$-->
69<script>window.__reactRouterContext.streamController.close();</script>
69<!--/$--><!--/$--></body></html>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.