Modern UI for Cloudflare R2 Storage: R2 Dashboard Chrome Extension
Last updated:
R2 Dashboard brings a modern, fast, and privacy‑respecting interface for Cloudflare R2 directly into your browser. It runs entirely on the client using Manifest V3, signs S3‑compatible requests locally, and minimizes permissions for a safer experience. This post tours the product and summarizes the key engineering patterns behind it.
Highlights
- Multi‑account setup with separate credentials for least privilege.
- Buckets view with object counts, sizes, and last sync time.
- Powerful object list: filter, preview, rename, delete, and folder operations.
- Fast uploads and safe downloads via presigned URLs.
- Hourly sync and daily snapshots with charts for trends and capacity planning.
- Runs fully in the browser service worker with offscreen parsing for large XML.
Architecture in MV3
The extension uses a background service worker for orchestration and offscreen
documents for heavy parsing.
UI surfaces live in popup.html
and options.html
. Reusable logic sits in utils/
and includes
cryptography, S3 helpers, logging, and a snapshot manager for charts. We keep business logic in modules and wire them from
the background or UI scripts as needed.
Client‑side SigV4 signing
Requests to Cloudflare R2 are signed client‑side with AWS SigV4 using WebCrypto. For details on canonical requests, string‑to‑sign, and pitfalls, see AWS SigV4 in the Browser for R2. The approach avoids servers, keeps credentials local, and enables a responsive UI.
// Signing sketch (see utils/s3-*.js)
const canonicalRequest = [method, path, query, canonicalHeaders + '\n', signedHeaders, payloadSha256].join('\n');
const hashed = await sha256(canonicalRequest);
const scope = `${date}/${region}/${service}/aws4_request`;
const stringToSign = ['AWS4-HMAC-SHA256', amzDate, scope, hashed].join('\n');
const signature = await hmac(deriveKey(secretAccessKey, date, region, service), stringToSign);
const auth = `AWS4-HMAC-SHA256 Credential=${accessKeyId}/${scope}, SignedHeaders=${signedHeaders}, Signature=${signature}`;
fetch(endpoint, { method, headers: { ...headers, Authorization: auth }});
Presigned downloads
Downloads use presigned URLs to reduce header complexity and improve compatibility with filenames. See
Presigned URLs for Fast and Safe R2 Downloads
for the MV3 chrome.downloads
flow and edge cases.
Parsing large S3 XML listings
Bucket listings can be large. We spin up an offscreen document to use DOMParser
safely in MV3 and stream results back to
the worker. See Parsing Large S3 XML Listings in MV3 with Offscreen for the pattern.
Snapshots and charts
A scheduler captures hourly deltas and summarizes daily snapshots for bucket/object counts and total size. These feed charts on the dashboard to surface growth trends and anomalies.
// utils/snapshot-manager.js (concept)
await snapshotManager.record({ accountId, bucket, objectCount, totalSize });
const series = await snapshotManager.series({ bucket, windowDays: 30 });
renderChart(series);
Security and privacy
- Credentials stay in Chrome
storage
and are encrypted at rest; nothing leaves your browser. - Minimal
manifest.json
permissions focused on storage, downloads, alarms, and limited hosts. - Logging goes through a centralized
utils/logger.js
for predictable diagnostics.
Product experience
- Search objects quickly with prefix and pagination controls.
- Inline rename and folder creation to keep you in flow.
- Bulk delete with confirmation and clear, informative toasts.
- Keyboard‑friendly navigation and accessible focus states.
If you are building a storage toolchain, the patterns above compose well in any MV3 extension: keep logic in modules, sign locally, use offscreen for parsing, and prefer presigned URLs for transfers.
Related reads: Minimal Permissions in MV3, Multi‑Account and Snapshotting, Create API Token and S3 Secret.