Guide
Why pre-signed URLs and not a proxy
The design decision behind the upload surface — zero connect0 bandwidth in the data path, capability bound by SigV4 + expiry. The cost / latency / trust tradeoffs.
5/18/2026 · connect0 · 3 min
The core connects data in without ever touching the bytes — pre-signed URLs are why.
Every upload to connect0 PUTs directly to Cloudflare R2. connect0's servers never touch the bytes. This is a deliberate choice — here's why we made it and what alternatives we rejected.
The pre-signed pattern
upload.create_link returns a URL like:
https://<account>.r2.cloudflarestorage.com/connect0-sandbox-fs/projects/<id>/data/foo.csv
?X-Amz-Algorithm=AWS4-HMAC-SHA256
&X-Amz-Credential=...
&X-Amz-Date=20260518T160000Z
&X-Amz-Expires=900
&X-Amz-SignedHeaders=content-type;host
&X-Amz-Signature=...
The signature is computed server-side by connect0 using R2 access keys. The user gets a URL that proves "this access-key-holder authorized this exact PUT to this exact key with these exact headers, valid for this exact window." No connect0 server is in the data path.
The alternative we rejected: server-side proxy
The other shape — and the more common one in less-considered SaaS — is POST /v1/upload to a connect0 endpoint that buffers the file and forwards it to R2. We didn't ship that. Reasons:
Bandwidth
A 100 MB upload through connect0 is 100 MB ingress and 100 MB egress on the GCP side. Multiply by every upload from every user. Pre-signed: zero bytes through us.
Latency
Proxy upload waits for the full file to land on connect0 before forwarding. Pre-signed: user's TCP connection is to R2 directly.
Reliability
Adding a hop adds failure modes — connect0 OOMs mid-upload, our proxy redeploys mid-stream, etc. R2 has its own SLA we trust.
Cost
GCP charges for egress. Cloudflare doesn't. Even at a few cents per gig, that compounds.
What pre-signed costs us
Complexity at the edges
We need a way to confirm uploads actually happened — the user's client could PUT and immediately tab away, or PUT fail at byte 99, or lie about success. That's why we have the R2 event-notification path: R2 itself reports completion through a Cloudflare Queue, and that's the only signal that flips upload_link.claimed_at.
Less server-side validation
A proxy could scan the file before persisting (size cap, magic-byte check, virus scan). We can't — by the time we know the bytes are in R2, they're already in the sandbox FS. We accept this; the sandbox is the trust boundary anyway. Antivirus inside a user-controlled sandbox would be theatre.
Asymmetric content-length
R2 doesn't honour S3's content-length-range policy. We work around it by signing exact Content-Length into URLs when the agent knows the size. For variable-size uploads, we rely on the post-claim bytes_received audit + the oversize flag.
What the capability gives an attacker if leaked
A pre-signed URL that escapes (Slack DM, log file, terminal scrollback) is a one-shot capability. Worst case if it's leaked:
- Overwrite one file at one path that the legitimate user was already going to write.
- Within a 15-min window (configurable up to 1h).
- Exact content-type (must match the signed header).
The audit row is always there — leaks show up in the ledger. We accept the residual risk because the alternative (longer-TTL or broader-scope credentials) is meaningfully worse.
Next steps
Where this fits
This is the Connect everything pillar of connect0 — the core that connects everything your company runs on. Pre-signed uploads are the design that lets data connect into the core at zero bandwidth and bounded risk. Start building → · All guides →