HumanOnlyWeb

🍁 writer of code. drinker of coffee. human on the web.

Emulating a Public R2 Bucket Locally with Nuxt

By HumanOnlyWeb

Wrangler 3+ removed the ability to easily emulate public R2 buckets locally. Here's a simple Nuxt server route workaround.

Background

When developing applications with Cloudflare R2 storage, you might run into a not-so-pleasing limitation: there's no native way to emulate a public R2 bucket during local development with Wrangler 3.

In Wrangler 2, locally uploaded files were saved with their original filenames, making it easy to serve them directly. Wrangler 3+ changed this behavior, and files are now stored with hashed names and managed through SQLite, breaking the straightforward workaround that developers relied on.

This has been a long-standing feature request with a lot of community interest (80+ reactions), and was first raised in mid 2023 by the VirtualWolf. Until Cloudflare implements native support, we need a workaround.

The Problem

When you configure an R2 bucket as public in production, Cloudflare serves files directly via a custom domain or the r2.dev subdomain. Locally, this doesn't exist—there's no built-in way to access your R2 files over HTTP during development.

The Solution

The workaround is simple: create a server route in your Nuxt application that proxies requests to R2. This route will only be used during local development, and in production, you can continue using your public R2 bucket URL.

Create a file at server/routes/assets/[filename].get.ts

export default defineEventHandler(async (event) => {
  // Only run this route in development.
  if (!import.meta.dev) return;

  const { filename } = getRouterParams(event);

  if (!filename) {
    throw createError({ status: 400, statusMessage: "Bad Request!" });
  }

  const r2 = event.context.cloudflare.env;
  const file = await r2.get(filename);

  if (!file) {
    throw createError({
      status: 404,
      statusMessage: "File not found!",
    });
  }

  setHeaders(event, { etag: file.etag });

  return file.body;
});

Now you can access your R2 files locally at /assets/your-filename

If you prefer the /api prefix, place the file at server/api/assets/[filename].get.ts instead.

Conclusion

While waiting for Cloudflare to add native public bucket emulation to Wrangler, this server route provides a clean workaround that integrates seamlessly with your Nuxt application. It's minimal, doesn't require additional dependencies, and gives you the same functionality you'd have with a public R2 bucket in production.

All Comments 0

Be the first to comment.