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

To upload a generated image to Amazon S3, send it to a bucket with an authorized AWS identity, or have your backend create a short-lived presigned URL so a browser can upload directly without receiving AWS credentials. Use a unique object key to avoid replacing another image, and consider multipart upload for large files or unreliable connections.

Choose the right upload method

An image in S3 is an object stored under a key in a bucket. The upload identity needs permission to write the object. The main design choice is where the image bytes travel and where AWS credentials live.

Method Where credentials live Where the image bytes travel Best fit
AWS SDK or CLI from a trusted server Server environment or role Image passes through the server to S3 Generated image is already on the server, or server-side processing is required
Presigned PUT URL Trusted backend only Client uploads directly to S3 Browser or client should not receive AWS credentials
Multipart upload SDK identity or backend that authorizes parts Large file is sent as independently retryable parts Large objects or connections where restarting a whole upload is costly

AWS documents a single PUT upload limit of 5 GB and recommends multipart upload for objects 100 MB or larger. Multipart supports objects from 5 MB to 50 TB. These are AWS service documentation limits and guidance, accessed September 30, 2026; they are not independently tested figures. The S3 console has a separate documented upload limit of 160 GB. AWS: Uploading objects AWS: Multipart upload overview

Upload from a trusted server with the AWS SDK

When your image generator runs on a server, upload from that server rather than putting long-lived AWS credentials in browser code. Give the server role or credentials only the S3 write access the application needs. The following Node.js example uses AWS SDK for JavaScript v3 and uploads an existing image file.

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

Install the SDK and configure access

Install the client package:

npm install @aws-sdk/client-s3

Configure AWS credentials through the standard SDK credential provider chain, such as an appropriately authorized role in the deployment environment. Set the bucket and region for your deployment; do not commit credentials to source control.

Upload an image file

import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
import { readFile } from "node:fs/promises";
import { randomUUID } from "node:crypto";

const region = process.env.AWS_REGION;
const bucket = process.env.S3_BUCKET;
if (!region || !bucket) throw new Error("Set AWS_REGION and S3_BUCKET");

const client = new S3Client({ region });
const filePath = "./generated-image.webp";
const body = await readFile(filePath);
const key = `generated/${randomUUID()}.webp`;

await client.send(new PutObjectCommand({
  Bucket: bucket,
  Key: key,
  Body: body,
  ContentType: "image/webp",
}));
console.log(`Uploaded s3://${bucket}/${key}`);

Use the actual media type of the generated file in ContentType, such as image/png or image/jpeg. The key is the object’s name within the bucket; the example uses a random identifier to reduce collision and overwrite risk. This example reads the entire file into memory, so for very large images prefer a streaming or multipart-capable upload approach.

Let a browser upload with a presigned URL

A presigned URL lets a trusted signer delegate a specific S3 operation temporarily. The browser receives the URL, not the signer’s AWS credentials. Your backend must still have permission for the underlying operation and should authenticate the user, validate the requested upload, and choose the object key itself. AWS describes presigned URLs as a way to allow someone to upload a specific object to a bucket. AWS: Uploading objects with presigned URLs

Backend: create the URL

Install the presigner package in addition to the S3 client:

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.

npm install @aws-sdk/client-s3 @aws-sdk/s3-request-presigner

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

const region = process.env.AWS_REGION;
const bucket = process.env.S3_BUCKET;
if (!region || !bucket) throw new Error("Set AWS_REGION and S3_BUCKET");

const s3 = new S3Client({ region });

// Call this only after authenticating the user and validating the upload request.
export async function createImageUpload() {
  const key = `generated/${randomUUID()}.png`;
  const command = new PutObjectCommand({
    Bucket: bucket,
    Key: key,
    ContentType: "image/png",
  });
  const uploadUrl = await getSignedUrl(s3, command, { expiresIn: 300 });
  return { uploadUrl, key, contentType: "image/png" };
}

The five-minute requested lifetime is an example, not a universal setting. Choose an expiry that allows the expected upload time while limiting the window in which the URL can be used. The signer’s credentials can expire or be revoked sooner, which can make the URL stop working before its requested expiry.

Browser: send the image bytes

Have the page request the upload details from your authenticated application endpoint, then PUT the image to the returned URL. The content type must match the one included when the URL was signed.

async function uploadGeneratedImage(blob) {
  const detailsResponse = await fetch("/api/image-upload", { method: "POST" });
  if (!detailsResponse.ok) throw new Error("Could not prepare image upload");
  const { uploadUrl, key, contentType } = await detailsResponse.json();

  const uploadResponse = await fetch(uploadUrl, {
    method: "PUT",
    headers: { "Content-Type": contentType },
    body: blob,
  });
  if (!uploadResponse.ok) {
    throw new Error(`S3 upload failed: ${uploadResponse.status}`);
  }
  return key;
}

For browser uploads, configure the bucket’s CORS policy to allow requests from your application’s origin, the PUT method, and headers the browser sends. CORS is a browser access-control requirement; it does not grant S3 write permission or replace the presigned authorization. Keep the bucket private unless public access is an intentional, separately designed requirement.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

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

Use multipart upload for large or failure-prone transfers

Multipart upload divides an object into parts that can be uploaded independently; S3 assembles the parts when the upload is completed. If a part fails, retransmit that part rather than restarting the entire object. AWS supports multipart uploads from 5 MB through 50 TB and recommends multipart at 100 MB or larger. AWS: Multipart upload overview

For Node.js, AWS SDK for JavaScript v3 includes @aws-sdk/lib-storage, a high-level upload abstraction that supports multipart uploads in Node.js and browsers. It is appropriate when the application or runtime can use the SDK to manage the upload. For a browser that must not receive AWS credentials, use a backend-authorized design rather than exposing an AWS SDK identity to the page; the backend can coordinate a multipart flow with authorized part URLs when needed.

Production multipart designs should handle failed parts and abandoned uploads. Arrange cleanup or abort handling for uploads that never complete, and consult current AWS lifecycle and SDK guidance for the exact implementation. AWS: Aborting multipart uploads

Protect object keys and presigned URLs

  • Use server-chosen keys. Do not let an untrusted client choose arbitrary keys that could overwrite another user’s object. Generate unique names or allocate keys within a user-specific namespace.
  • Treat the URL like a temporary password. A presigned URL is a bearer authorization: anyone who obtains it can use its permitted operation while it remains valid.
  • Understand reuse and replacement. A presigned URL may be reused until expiration. Uploading to an existing key replaces that object; use unique keys or deliberately designed versioning-aware behavior if replacement is not wanted.
  • Keep expiry appropriate. A URL stops working at its expiration or when its signing credentials expire or are revoked, whichever comes first.
  • Limit signer permissions. The signer needs permission for the S3 action, bucket, and object involved. Presigning does not bypass that permission requirement.

These behaviors are described in AWS’s presigned URL documentation. AWS: Uploading objects with presigned URLs

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Check that the uploaded image is intact

For integrity validation, AWS Signature Version 4 presigned uploads support checksum algorithms when the corresponding checksum header is included. Multipart uploads can validate a supplied full-object checksum on the server side and reject a mismatch. A multipart ETag is not automatically the full object’s MD5 hash, so do not treat it as a universal content checksum. AWS: Checking object integrity AWS: Multipart upload overview

Common upload errors and fixes

  • Access denied or a 403 response: confirm the signer or SDK identity has permission for the target bucket and key, and check that the URL has not expired or lost valid signing credentials.
  • Signature mismatch: ensure the browser sends the same method and signed headers, including the expected content type. Do not alter or re-encode the presigned URL.
  • Browser reports a CORS failure: allow the app’s exact origin, PUT method, and required request headers in the bucket’s CORS configuration. CORS does not itself authorize the upload.
  • Upload works once, then fails: the URL may have expired, or its signing credentials may no longer be valid. Request a fresh URL from the backend.
  • Another image unexpectedly disappears: the new upload used an existing key. Issue unique keys or use an intentional versioning and replacement policy.
  • Large upload fails or takes too long: use a multipart-capable approach so parts can be retried independently, and account for cleanup of abandoned multipart uploads.

Or skip the browser setup

If the job is to capture a webpage image rather than upload an image your application generated, ScreenshotNeo can return a clean screenshot or PDF through one GET request. Cookie and consent banners are accepted and removed along with known newsletter popups and chat widgets before capture; those steps can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, with verdict and billing information in response headers. Its MCP server gives AI agents tools for screenshots and PDFs.

Example cURL request for a WebP screenshot:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

See the ScreenshotNeo API documentation for request options. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000. ScreenshotNeo is useful for webpage capture, not as a replacement for storing generated image files in your S3 bucket. Sign up for 1,000 free screenshots a month with no card.

Frequently asked questions

Can I upload a generated image directly from a browser to S3?

Yes. A backend can issue a presigned PUT URL for a specific object key, and the browser can PUT the image bytes to it without receiving AWS credentials.

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

Does a presigned URL make the bucket public?

No. It grants temporary authority for the specific signed operation; it does not make the bucket generally public.

Should I use the AWS CLI or an SDK?

Either works for a trusted environment with authorized credentials. An SDK is convenient when upload is part of application code; the CLI suits manual or scripted operations.

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.