Upload
Upload a file directly to R2 via a presigned URL. The server generates the URL, then the browser uploads directly to R2 — the file never passes through the server.
Database engines, connection patterns, type systems, and data strategies.
Upload a file directly to R2 via a presigned URL. The server generates the URL, then the browser uploads directly to R2 — the file never passes through the server.
Fetch a byte range from showcase/large/padded.bin (1 MB repeating pattern). R2 supports the Range HTTP header for partial reads.
How presigned URLs lock Content-Type into the signature, preventing type-mismatch attacks.
Presigned URL was generated with Content-Type: image/png. Client sends Content-Type: image/png.
200 OK — Upload succeedsPresigned URL was generated with Content-Type: image/png. Client sends Content-Type: text/html.
403 SignatureDoesNotMatch — Rejected// Server: Content-Type is baked into the signature
const command = new PutObjectCommand({
Bucket: BUCKET,
Key: key,
ContentType: mimeType, // Locked into signature
});
const url = await getSignedUrl(s3, command, {
expiresIn: 300,
});
// Client: Must send the exact same Content-Type
fetch(url, {
method: 'PUT',
headers: { 'Content-Type': mimeType },
body: file,
});This prevents XSS attacks where an attacker could upload an HTML file disguised as an image. The signature ensures the Content-Type cannot be changed after the URL is generated.