Hardware FixRecommendedDevice not working? Your driver may be the problemCheck updates for common hardware issues.Fix DriversOctober DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsSlow PC?RecommendedPC slow today? Run a repair scan before it gets worseResolve common Windows issues and optimize system performance.Scan Now×
Skip to content
Amazon S3

How to Upload Screenshots to Amazon S3, Cloudflare R2, or Backblaze B2

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The safest browser workflow is server-signing followed by a direct upload. Keep cloud credentials on your server, validate the screenshot there, create a short-lived presigned URL for one object key, and let the browser send the image with an HTTP PUT. Amazon S3 and Cloudflare R2 use this pattern. Backblaze B2’s native API instead gives you a temporary upload URL with b2_get_upload_url, followed by a raw-byte b2_upload_file request.

This guide gives complete server, browser, cURL, Python, and Node.js examples, then compares limits, retries, CORS, visibility, and failure modes.

The upload design that works for all three providers

  1. Receive metadata, not credentials, from the browser. Send the selected file’s MIME type and size to your application server.
  2. Validate before signing. Allow only the image types and maximum size your application needs. Do not trust a user-supplied filename.
  3. Create a collision-resistant key. A pattern such as screenshots/{userId}/{uuid}.png prevents one upload from silently replacing another.
  4. Sign one operation. Bind the bucket, key, HTTP method, and expected Content-Type to a short-lived URL.
  5. Upload directly. The browser uses PUT with exactly the headers that were signed.
  6. Verify completion. Have your server issue a HEAD request or use the provider SDK before marking the screenshot complete in your database.

A presigned URL carries the permissions of the identity that created it. Anyone who obtains it can use that one authorization until it expires, so return it only over HTTPS, keep its lifetime short, and never put permanent cloud keys in JavaScript.

Amazon S3: presigned PUT upload

Server-side signer in Node.js

The signer needs permission to perform the underlying s3:PutObject operation. This example accepts only PNG uploads and gives the browser a five-minute URL.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
#1 Best Overall
Sale
Seagate 2TB Portable Hard Drive | USB 3.0 (STGX2000400)
  • Easily store and access 2TB to content on the go with the Seagate Portable Drive, a USB external hard drive
  • Designed to work with Windows or Mac computers, this external hard drive makes backup a snap just drag and drop
  • To get set up, connect the portable hard drive to a computer for automatic recognition no software required
  • This USB drive provides plug and play simplicity with the included 18 inch USB 3.0 cable
  • The available storage capacity may vary.
import crypto from 'node:crypto';
import express from 'express';
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';

const app = express();
app.use(express.json());
const s3 = new S3Client({ region: process.env.AWS_REGION });
const bucket = process.env.S3_BUCKET;

app.post('/api/screenshot-upload', async (req, res) => {
  const { contentType, size } = req.body;
  if (contentType !== 'image/png' || !Number.isInteger(size) || size < 1 || size > 10 * 1024 * 1024) {
    return res.status(400).json({ error: 'PNG required and file must be 10 MB or smaller' });
  }
  const userId = req.user.id; // Obtain this from your authenticated session
  const key = `screenshots/${userId}/${crypto.randomUUID()}.png`;
  const command = new PutObjectCommand({
    Bucket: bucket,
    Key: key,
    ContentType: contentType
  });
  const url = await getSignedUrl(s3, command, { expiresIn: 300 });
  res.json({ url, key, contentType });
});

app.listen(3000);

The URL authorizes one key. Uploading another image to an existing S3 key replaces that object, so never derive keys solely from an original filename.

Browser upload

async function uploadScreenshot(file) {
  const ticket = await fetch('/api/screenshot-upload', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ contentType: file.type, size: file.size })
  }).then(r => r.json());

  const response = await fetch(ticket.url, {
    method: 'PUT',
    headers: { 'Content-Type': ticket.contentType },
    body: file
  });
  if (!response.ok) throw new Error(`S3 upload failed: ${response.status}`);
  await fetch('/api/screenshot-upload/complete', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ key: ticket.key })
  });
}

For a browser origin, configure the bucket CORS rule for the exact origins you operate. Allow PUT and the signed request headers; expose ETag only if your client needs to read it.

cURL and Python clients

curl -X PUT -H 'Content-Type: image/png' --upload-file shot.png "$PRESIGNED_URL"
import boto3
import uuid

s3 = boto3.client('s3', region_name='us-east-1')
key = f'screenshots/user-123/{uuid.uuid4()}.png'
url = s3.generate_presigned_url(
    'put_object',
    Params={'Bucket': 'my-screenshot-bucket', 'Key': key, 'ContentType': 'image/png'},
    ExpiresIn=300,
    HttpMethod='PUT'
)
print(url)
# The client must PUT the bytes with Content-Type: image/png.

Signature Version 4 presigned requests can also bind checksum headers. If you use one, calculate the checksum before signing and send the identical header with the upload.

Cloudflare R2: S3-compatible signing with important differences

Configure the R2 client

R2 uses the AWS SDK protocol, but the endpoint is your account’s R2 endpoint, the region is auto, and authentication uses an R2 API token. A one-hour expiry appears in Cloudflare’s example; choose a shorter lifetime when practical.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
import crypto from 'node:crypto';
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';

const r2 = new S3Client({
  region: 'auto',
  endpoint: `https://${process.env.R2_ACCOUNT_ID}.r2.cloudflarestorage.com`,
  credentials: {
    accessKeyId: process.env.R2_ACCESS_KEY_ID,
    secretAccessKey: process.env.R2_SECRET_ACCESS_KEY
  }
});

const key = `screenshots/user-123/${crypto.randomUUID()}.png`;
const command = new PutObjectCommand({
  Bucket: process.env.R2_BUCKET,
  Key: key,
  ContentType: 'image/png'
});
const url = await getSignedUrl(r2, command, { expiresIn: 600 });
console.log(JSON.stringify({ url, key }));

The browser code is the same as S3, but the Content-Type must exactly match the value signed by R2. R2 presigned URLs support GET, HEAD, PUT, and DELETE. They are bearer tokens and may expire from one second to seven days. R2 does not support HTML-form POST uploads for presigned URLs, so use the signed PUT flow.

Rank #2
Seagate Portable 1TB External Hard Drive HDD – USB 3.0 for PC, Mac, PlayStation, & Xbox, 1-Year Rescue Service (STGX1000400) , Black
  • Easily store and access 1TB to content on the go with the Seagate Portable Drive, a USB external hard drive.Specific uses: Personal
  • Designed to work with Windows or Mac computers, this external hard drive makes backup a snap just drag and drop. Reformatting may be required for Mac
  • To get set up, connect the portable hard drive to a computer for automatic recognition no software required
  • This USB drive provides plug and play simplicity with the included 18 inch USB 3.0 cable
  • The available storage capacity may vary.

Size and multipart choices

Cloudflare documents a 5 GiB limit for one upload and multipart objects up to 5 TiB, with as many as 10,000 parts. Multipart parts must be 5 MiB to 5 GiB. Multipart uploads can resume and run in parallel; a failed single PUT has to start over. Abort incomplete multipart uploads so abandoned parts do not remain indefinitely.

R2 browser checklist

  • Add the precise application origins to the bucket CORS configuration.
  • Allow PUT and the headers your signature includes, especially Content-Type.
  • Expose ETag only when the browser must read it.
  • Never expose the R2 API token; only return the temporary URL and object key.

Backblaze B2: use the Native API upload URL

B2’s native flow is not the same as copying an S3 presigned example. Your server first calls b2_get_upload_url for a bucket, then sends the screenshot bytes as the raw body to b2_upload_file. Include Content-Length; chunked transfer encoding is unsupported.

Python example

import hashlib
import os
import requests

# B2_AUTH_URL and credentials are supplied by your deployment environment.
auth = requests.get(
    os.environ['B2_AUTH_URL'],
    auth=(os.environ['B2_KEY_ID'], os.environ['B2_APPLICATION_KEY']),
    timeout=30
)
auth.raise_for_status()
a = auth.json()

upload_info = requests.post(
    a['apiUrl'] + '/b2api/v3/b2_get_upload_url',
    headers={'Authorization': a['authorizationToken']},
    json={'bucketId': os.environ['B2_BUCKET_ID']},
    timeout=30
)
upload_info.raise_for_status()
u = upload_info.json()

data = open('shot.png', 'rb').read()
file_name = 'screenshots/user-123/shot.png'
headers = {
    'Authorization': u['authorizationToken'],
    'X-Bz-File-Name': file_name,
    'Content-Type': 'image/png',
    'Content-Length': str(len(data)),
    'X-Bz-Content-Sha1': hashlib.sha1(data).hexdigest()
}
result = requests.post(u['uploadUrl'], headers=headers, data=data, timeout=90)
result.raise_for_status()
print(result.json()['fileId'])

In production, your server should perform both API calls and return a short-lived application-specific upload ticket rather than B2 credentials. B2 returns a unique file ID. Server-side encryption defaults to SSE-B2 when enabled.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Manual console upload

For an occasional file, the Backblaze web console lets you drag an image into a bucket. Its documented single-file limit is 500 MB. A public bucket is publicly readable, never publicly writable; uploading still requires credentials. B2 also provides S3-style URLs for public objects. Do not put PHI or PII in bucket names, object names, folder names, or metadata.

S3, R2, and B2 compared for screenshot uploads

Question Amazon S3 Cloudflare R2 Backblaze B2
Browser path Presigned PUT Presigned PUT; HTML-form POST presigned uploads are not supported Native upload URL, then raw-body upload
Credential exposure Only temporary URL reaches the browser Only temporary URL reaches the browser; treat it as a bearer token Keep application credentials and upload authorization on the server
Content-Type control Bind it when signing; send the same value Bind it when signing; exact match required Send it in the upload headers
Single-object limit Not stated in the supplied provider material 5 GiB single upload (Cloudflare, 2026) Not stated for the API in the supplied material; console limit is 500 MB per file
Multipart Use the provider’s multipart API for large or unreliable files Up to 5 TiB, 5 MiB–5 GiB parts, 10,000 parts (Cloudflare, 2026) Use the B2 multipart API when a single request is unsuitable
Retry behavior Retry a failed small PUT; multipart for resumability Single PUT restarts; multipart resumes and parallelizes Retry with a fresh upload authorization when required
Pricing and egress Check current regional pricing Check current R2 pricing and egress terms Check current B2 pricing and egress terms

Security and operational checklist

  • Authenticate the user before issuing a URL and derive the key from server-side identity.
  • Validate the declared MIME type, byte length, and (where necessary) the file signature or decoded image.
  • Use a random ID in every key. A presigned upload to an existing key replaces that object.
  • Sign only the required headers and reject a completion callback for an unexpected key or size.
  • Use private buckets unless public delivery is an explicit requirement. Store display metadata, ownership, and moderation state in your database rather than trusting object names.
  • Set CORS to exact origins, not a blanket wildcard, when the bucket contains user data.
  • For large files, use multipart uploads and a cleanup policy for incomplete uploads.
  • Verify with HEAD or the provider SDK before reporting success to the user.

Troubleshooting common failures

HTTP 403 or “AccessDenied”

The signing identity may lack permission for the bucket/key, the URL may be expired, or a bucket policy may deny the request. Re-sign with the intended key and verify IAM or token permissions.

Rank #3
Sale
WD 2TB Elements Portable External Hard Drive for Windows, USB 3.2 Gen 1/USB 3.0 for PC & Mac, Plug and Play Ready - WDBU6Y0020BBK-WESN
  • High capacity in a small enclosure – The small, lightweight design offers up to 6TB* capacity, making WD Elements portable hard drives the ideal companion for consumers on the go.
  • Plug-and-play expandability
  • Vast capacities up to 6TB[1] to store your photos, videos, music, important documents and more
  • SuperSpeed USB 3.2 Gen 1 (5Gbps)

“SignatureDoesNotMatch”

The method, region, endpoint, key, query string, or a signed header differs from what the client sent. For R2, confirm region: 'auto' and the account endpoint. Do not add or alter a signed Content-Type.

Browser reports a CORS error

CORS is evaluated by the storage service, not your application server. Add the exact origin, allow PUT and the requested headers, then retry from a fresh browser request.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Upload succeeds but the image is unusable

Check that the bytes are the original file, the content type matches the format, and no proxy converted the request to text. A server-side HEAD check should confirm length and metadata.

R2 form upload fails

Presigned HTML-form POST uploads are not supported by R2. Generate a presigned URL and use an HTTP PUT.

B2 rejects the request for transfer encoding

Send a concrete Content-Length. B2’s b2_upload_file and b2_upload_part calls do not accept chunked transfer encoding.

Large or unreliable uploads restart

That is expected for a single PUT. Switch to multipart, retain the upload ID and completed part list, retry failed parts, and abort abandoned uploads.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The same screenshot keeps changing

Two clients are writing the same object key. Include the authenticated user ID and a UUID in every key, and treat the original filename as display metadata only.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Performance, reliability, and cost decisions

Direct browser uploads keep image bytes off your application server, reducing bandwidth and request time there. A short presigned lifetime limits misuse but must exceed the user’s expected upload duration; renew the ticket rather than extending it to hours. Retry small uploads with exponential backoff. For large files or mobile connections, multipart is the reliability choice because individual parts can be retried or uploaded in parallel.

Storage, request, transfer, and egress charges vary by provider, region, retrieval pattern, and date. The supplied provider material does not establish current prices, so compare each provider’s current pricing page for your region before choosing on cost. Measure the bytes your application actually serves, not merely the screenshot count.

Or skip the browser setup

If you still need the screenshot itself, ScreenshotNeo is an alternative to try first: it returns a clean image or PDF from one request, removes consent banners, newsletter popups, and chat widgets before capture, and bills only clean shots.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

cURL (full options are in the ScreenshotNeo documentation):

Best Value
Sale
UnionSine 1TB Ultra Slim Portable External Hard Drive HDD-USB 3.0
  • 【Upgraded version】 - The mirror logo strip is combined with the striped non-slip design. The rounded corners of the shell are more suitable for holding. The strips play a heat dissipation function to ensure a stable and fast transmission process.
  • 【Ultra-thin and quiet】 - The motherboard adopts JMicron 578 noise-free solution, giving you a quiet working environment. Lightweight and portable size designed to fit in your pocket for easy portability.
  • 【Ultra-Fast Data Transfers】 - Pairing this external hard drive with JMicron 578 solution USB 3.0 and USB 2.0 interfaces enables blazing-fast data transfer. It boasts theoretical read speeds of up to 125MB/s and write speeds of up to 103MB/s.
  • 【Plug and Play】 - With no software to install, just plug it in and the drive is ready to use.The hard disk chip is wrapped with an aluminum anti-interference layer to increase heat dissipation and protect data.
  • 【What You Get】 - 1 x Portable Hard Drive, 1 x USB 3.0 Cable, 1 x User Manual, Gift-type shell packaging ,Three-year manufacturer's warranty and free technical support services.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

Python:

import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)

Node.js:

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing result. Its MCP server lets Claude, Cursor, or another MCP client call take_screenshot, get_page_info, and capture_pdf. The Free plan includes 1,000 screenshots each month with no card; paid plans start at $5 for 3,000. After receiving the image, upload it to S3, R2, or B2 using the methods above. Create a free ScreenshotNeo account.

Frequently Asked Questions

Can I reuse one presigned URL for several screenshots?

A URL is authorized for its specific operation and object key. Issue a new URL per object so retries and ownership checks remain unambiguous.

Should screenshot object names contain a user’s email address?

No. Use an internal user ID and a random identifier in the key, and keep human-readable labels in your application database.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

When should I make a screenshot bucket public?

Only when anyone who obtains the object URL is allowed to read it. Keep user uploads private and deliver them through authenticated application routes when access must be controlled.

Quick Recap

SaleBestseller No. 1
Seagate 2TB Portable Hard Drive | USB 3.0 (STGX2000400)
Seagate 2TB Portable Hard Drive | USB 3.0 (STGX2000400)
This USB drive provides plug and play simplicity with the included 18 inch USB 3.0 cable; The available storage capacity may vary.
$119.99
Bestseller No. 2
Seagate Portable 1TB External Hard Drive HDD – USB 3.0 for PC, Mac, PlayStation, & Xbox, 1-Year Rescue Service (STGX1000400) , Black
Seagate Portable 1TB External Hard Drive HDD – USB 3.0 for PC, Mac, PlayStation, & Xbox, 1-Year Rescue Service (STGX1000400) , Black
This USB drive provides plug and play simplicity with the included 18 inch USB 3.0 cable; The available storage capacity may vary.
$119.80
SaleBestseller No. 3

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Read next

Recommended PC Tool
Recommended PC Tool
Windows Errors? Fix Them Before They SpreadFree repair scan
Outdated Drivers Are Slowing You DownFree scan - exact matches

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.