Skip to content

A browser upload to S3 fails with CORS

2 min read · main part only; folded sections are optional

After this you can answer: How do you distinguish an S3 CORS failure from a signed upload permission failure?

Troubleshooting scenario: a poster upload fails in the browser. S3’s OPTIONS response says “This CORS request is not allowed.” CORS controls which website origins a browser may read responses from; it does not grant access to private objects.

The browser enforces CORS; S3 answers the preflight, an OPTIONS request asking whether the planned PUT is allowed from this origin. Reach succeeded if S3 replied. This is a browser cross-origin check, not a password. If OPTIONS passes and PUT fails, check Allowed/signature separately: IAM missing allow and explicit deny are authorization failures, not CORS fixes.

flowchart TB
A["Browser upload failed"] --> B{"OPTIONS succeeded?"}
B -->|"no"| C["Match origin, PUT and requested headers"]
B -->|"yes"| D{"PUT succeeded?"}
D -->|"no"| E["Read S3 signature or authorization error"]
D -->|"yes"| F["Check exposed headers and completion API"]

Ranked for the quoted preflight rejection.

  1. The page’s scheme, hostname or port differs from the configured appUrl origin, perhaps after an ALB/CloudFront mode change.
  2. The requested method or header is absent from the bucket CORS rule.
  3. The preflight passed, but a PUT authorization error is hidden from script by missing CORS response headers. Inspect both requests rather than trusting the console summary.

Browser Network → OPTIONS: compare Origin, Access-Control-Request-Method and Access-Control-Request-Headers with S3 → bucket → Permissions → CORS. Then inspect PUT. Do not copy its signed query string into a report.

Align createAssetCors with the actual web origin. Current rules allow GET/PUT and content-type, content-length, x-amz-server-side-encryption; expose ETag, cache preflight 120 seconds. Keep the upload hook’s returned signed headers and credentials: "omit".

Run a browser poster-upload check after origin/mode changes. Verify OPTIONS, PUT and API publication, while also testing an unauthorized origin. Broadening IAM cannot repair a preflight.

infra/src/tickets/cors.ts → createAssetCors; Inspect bucket CORS →.

Advanced: Three different checks in one upload

The authorized start API gives an admin a short-lived capability. S3 CORS lets the browser perform its cross-origin request. S3 signature and policy checks authorize the object PUT. A successful PUT still does not publish a poster: the completion API verifies stored bytes, then copies to a separate published key. All three stages need evidence. S3 handles OPTIONS from the CORS configuration; allowedMethods lists PUT rather than OPTIONS. See AWS’s CORS troubleshooting rules. apps/web/src/features/events/hooks/use-upload-poster.ts → useUploadPoster; apps/api/src/services/posters.ts → complete.

Read this book online: https://book.theversehub.in/