# Cloudflare R2 Bucket Management And Access

> Use this skill to configure Cloudflare R2 buckets, access control, lifecycle policies, public/private assets, and integration with Hono Workers using read/write/delete/list operations and signed-access patterns.

- Skill: `majiayu000/cloudflare-r2-bucket-management-and-access` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds add majiayu000/cloudflare-r2-bucket-management-and-access`
- Raw SKILL.md: https://api.skillmd.com/api/skills/majiayu000/cloudflare-r2-bucket-management-and-access/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Integrations & APIs
- Author: majiayu000 (https://skillmd.com/u/majiayu000)
- Updated: 2026-09-09
- Page: https://skillmd.com/skills/majiayu000/cloudflare-r2-bucket-management-and-access

---


# Cloudflare R2 Bucket Management & Access Skill

## Purpose

You are the assistant responsible for **managing R2 storage in production** including:

- Bucket provisioning & environment separation
- Public/private access strategy
- Upload/download/stream patterns
- Lifecycle rules & retention policies
- Signed URL access patterns
- Asset optimization + access performance

Use this skill when handling Cloudflare **R2 + Hono Worker integration** beyond basic uploads.

Do **not** use this skill for:

- D1 schema or migrations → `cloudflare-d1-migrations-and-production-seeding`
- Core Hono routing → `hono-app-scaffold`
- Auth logic → `hono-authentication`


---

## Environment-Aware Bucket Setup

This skill knows how to structure R2 per environment:

```toml
[[r2_buckets]]
binding = "BUCKET"
bucket_name = "my-bucket-dev"
preview_bucket_name = "my-bucket-dev"

[env.staging]
[[env.staging.r2_buckets]]
binding = "BUCKET"
bucket_name = "my-bucket-staging"

[env.production]
[[env.production.r2_buckets]]
binding = "BUCKET"
bucket_name = "my-bucket-prod"
```

Claude should:

- Default to **separate buckets per environment**
- Suggest mirroring structure between staging & prod
- Prevent mixing prod bucket during dev


---

## R2 Access Permission Strategy

R2 can be accessed in multiple ways — this skill helps choose between:

| Mode | Use Case |
|---|---|
| **Private (default)** | User uploads, internal assets |
| **Public Read** | Images, static assets, public files |
| **Signed-access URLs** | Limited-time external file sharing |
| **Proxy download route via Hono** | Controlled rule-based access |

This skill selects approach based on context.

**Best practice:** Keep buckets **private**, expose objects through **Worker/Routes** or **signed URLs**.


---

## Private Access (Recommended Default)

Use Cloudflare Worker as controlled access:

```ts
app.get("/file/:key", async (c) => {
  const key = decodeURIComponent(c.req.param("key"));

  const obj = await c.env.BUCKET.get(key);
  if (!obj) return c.notFound();

  return new Response(obj.body, {
    headers: { "Content-Type": obj.httpMetadata?.contentType ?? "application/octet-stream" }
  });
});
```

This enables auth-check:

```ts
if (!c.get("user")) return c.json({ auth: false }, 401);
```


---

## Public Access Mode

Enable public read with bucket policy:

```bash
wrangler r2 bucket policy put BUCKET << 'EOF'
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": "*",
      "Action": "r2:GetObject",
      "Resource": "BUCKET/*"
    }
  ]
}
EOF
```

Use only for assets intended to be publicly hosted (CDN optimized).


---

## Upload Handling (secure)

```ts
const file = await c.req.formData().then(f => f.get("file") as File);
const key = `users/${c.get("user")?.id}/${Date.now()}-${file.name}`;

await c.env.BUCKET.put(key, await file.arrayBuffer(), {
  httpMetadata: { contentType: file.type }
});

return c.json({ key, url: `/file/${encodeURIComponent(key)}` });
```

The skill ensures safe naming conventions:

- No raw user-supplied keys
- Timestamp or UUID prefix
- Supports multi-tenant structures


---

## R2 Lifecycle Policies

Claude may apply retention or expiration policy suggestions:

| Policy | Benefit |
|---|---|
| Auto-delete after X days | Temporary file cleanup |
| Move to cold storage after X | Cost reduction |
| Prevent overwrite unless flagged | Audit safety |

Example JSON policy reference:

```json
{
  "Rules": [
    {
      "ID": "auto-expire-temp",
      "Filter": { "Prefix": "temp/" },
      "Expiration": { "Days": 30 }
    }
  ]
}
```


---

## Signed URL Access (download/share)

Cloudflare does not sign natively — we generate JWT-contained key + expiry:

```ts
import { SignJWT, jwtVerify } from "jose";

export async function createSignedURL(key: string, expSec: number, secret: string) {
  const token = await new SignJWT({ key })
    .setProtectedHeader({ alg: "HS256" })
    .setExpirationTime(Math.floor(Date.now()/1000) + expSec)
    .sign(new TextEncoder().encode(secret));

  return `/download/${token}`;
}

app.get("/download/:token", async (c) => {
  const secret = c.env.JWT_SECRET;
  const { payload } = await jwtVerify(c.req.param("token"), new TextEncoder().encode(secret));
  const obj = await c.env.BUCKET.get(payload.key);
  if(!obj) return c.notFound();
  return new Response(obj.body);
});
```

This simulates **presigned URLs safely**.


---

## Directory-like Prefixing

Claude may suggest folder-style structure for organization:

```
/public/assets/*
/users/[id]/uploads/*
/tenants/[id]/invoices/*
/tmp/uploads/*
```

Naming rules enforced by this skill:

- Avoid exposing tenant IDs publicly unless hashed
- PascalCase or kebab-case predictable
- Versionable storage layout (`v1/avatars`) for migrations


---

## Listing + Search

```ts
const result = await c.env.BUCKET.list({ prefix: `users/${id}/` });
return c.json(result.objects.map(o => ({ key: o.key, size: o.size, date: o.uploaded })));
```

Skill advises pagination if results exceed limits.


---

## CI/CD Hooks For R2 Deployments

This skill suggests combining with future CI/CD workflow:

```bash
wrangler deploy --env production
```
Then apply bucket policy or migration tasks post-deploy.

For automation:
- cache invalidation
- namespace provisioning
- retention checks


---

## Example Prompts That Activate This Skill

- “Store files in R2 with security rules.”  
- “Make public CDN bucket for static assets.”  
- “Generate signed URLs for temporary downloads.”  
- “Organize tenant bucket structure cleanly.”  
- “Set expiration policy for old file uploads.”  


---

## Interactions With Other Skills

| Skill | How it connects |
|---|---|
| `hono-r2-integration` | This skill expands it with policy, access control, public/private modes |
| `cloudflare-worker-deployment` | Ensures R2 bindings are correctly mapped across envs |
| `cloudflare-d1-migrations-and-production-seeding` | Not DB schema — but both require env-controlled versioning |
| `hono-authentication` | User-based folder ownership, signed URL verification |

