Make one real URL before you keep reading

Upload a file up to 10 MB. FilePost stores it, returns the public CDN URL, and creates your free API key in the same step.

Cloudflare R2 Alternative When You Want an Upload API, Not a Storage Project

August 9, 2026 · 10 min read

The short answer: Cloudflare R2 is excellent object storage. FilePost is a finished file-upload product. Choose R2 when storage infrastructure is something your team wants to own. Choose FilePost when storage infrastructure is something your team wants to stop thinking about.

R2's pricing is hard to beat: low per-GB storage, free Internet egress and generous free operation allowances. But an R2 bucket is not automatically a production upload API with durable public URLs. Your application still owns credentials, S3 request signing, object keys, CORS, public access, custom-domain setup, caching policy, retries, metadata and lifecycle behavior.

Cloudflare R2 vs FilePost

DecisionFilePostCloudflare R2
Product layerManaged upload and public delivery APIS3-compatible object storage
First uploadOne multipart requestS3 SDK/Worker or presigned PUT flow
Production public URLReturned immediatelyConfigure a custom domain and public bucket behavior
Browser uploadUse trial journey or your server-side API integrationGenerate presigned URL and configure bucket CORS
PricingPlans based on upload count, storage and file sizeStorage plus Class A/B operations; Internet egress free
File managementList, inspect and delete through FilePost API/dashboardOwn S3 object operations and management UI
AutomationDirect signed HTTP webhook with retriesEvent notifications and Queues/Workers architecture
ControlOpinionated managed serviceBucket ownership and infrastructure control

What It Takes to Turn R2 into "Upload and Get a URL"

A production browser-to-R2 upload commonly needs two requests and several infrastructure decisions:

  1. Create the R2 bucket and scoped API credentials.
  2. Choose an object-key convention and collision strategy.
  3. Build a backend endpoint or Worker that signs a PutObject request.
  4. Configure CORS for the browser origin.
  5. Return the temporary presigned PUT URL to the browser.
  6. Upload the bytes to that URL.
  7. Configure public read access separately.
  8. Connect a custom domain for production delivery.
  9. Construct and store the final public object URL.
  10. Add validation, retries, metadata, cleanup and event handling.

Cloudflare's official presigned URL documentation lists the required account ID, bucket, object path, operation, expiry, R2 credentials and AWS Signature Version 4 implementation. It also notes that presigned URLs work with the S3 API domain, not custom domains.

FilePost makes the product-level operation one request:

curl -X POST https://upload.filepost.dev/v1/upload \
  -H "X-API-Key: $FILEPOST_API_KEY" \
  -F "file=@build-artifact.zip"

The response includes the public CDN URL. No bucket name, account ID, region placeholder, object URL assembly or second upload request is required.

Where R2 Is Better

If your team already knows S3 and Cloudflare, FilePost may be an unnecessary abstraction. FilePost is selling avoided engineering and operational work, not cheaper object bytes.

Where FilePost Is Better

Public Delivery Is a Separate R2 Decision

R2 buckets are private by default. Cloudflare's public bucket documentation offers two exposure methods:

Cloudflare also notes that only certain file types are cached by default unless you configure broader cache behavior. R2 gives you the pieces; you decide how the public delivery product should work.

Price Comparison Without Pretending the Products Are Equal

The official R2 pricing page, checked August 9, 2026, listed:

FilePost Lite is $4 per month for 300 uploads, 100 MB files and 10 GB storage. Starter is $9 for 1,500 uploads and 200 MB files. R2 will often be cheaper in raw infrastructure dollars. FilePost can be cheaper in engineering time when bucket and delivery configuration are not part of your product's advantage.

Code Comparison: Direct Browser Upload

R2 backend: generate the presigned URL

import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";

const r2 = 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,
  },
});

const key = `uploads/${crypto.randomUUID()}-${fileName}`;
const uploadUrl = await getSignedUrl(
  r2,
  new PutObjectCommand({
    Bucket: "uploads",
    Key: key,
    ContentType: contentType,
  }),
  { expiresIn: 900 }
);

R2 browser: upload the bytes

await fetch(uploadUrl, {
  method: "PUT",
  headers: { "Content-Type": file.type },
  body: file,
});

const publicUrl = `https://files.example.com/${key}`;

FilePost server: upload and receive the URL

const form = new FormData();
form.append("file", file);

const response = await fetch("https://upload.filepost.dev/v1/upload", {
  method: "POST",
  headers: { "X-API-Key": process.env.FILEPOST_API_KEY },
  body: form,
});

const { url, file_id } = await response.json();

Migration Checklist

  1. Identify the paths that only use PutObject plus a public URL.
  2. Keep R2 for private objects, very large files or high-scale storage pipelines.
  3. Replace the signing endpoint and client PUT with one FilePost server request.
  4. Store FilePost's returned URL and file ID instead of constructing an object URL.
  5. Replace queue/Worker notifications with the signed FilePost webhook where appropriate.
  6. Leave old R2 objects and custom-domain URLs available until references are migrated.

The Decision Rule

Choose R2 when object storage is a core infrastructure competency and cost/control at scale outweigh setup work.

Choose FilePost when file handling is a supporting feature and your real requirement ends at a durable public URL.

Compare the outcome, not the architecture

The upload form above creates a real URL without a bucket or signing endpoint. If that is the complete result your feature needs, FilePost is the smaller integration.

Get a free API key