Guide: S3/R2 CORS Generator & Debugger
Back to toolWhat is this tool?
This is a browser-side S3 CORS generator and Cloudflare R2 CORS debugger for bucket configs. It builds the shared JSON format used by AWS S3 and Cloudflare R2, then checks a real request shape against an existing config: frontend origin, HTTP method, request headers, exposed response headers, and browser console error text. It is built for common object-storage workflows such as S3 presigned URL uploads, Cloudflare R2 browser uploads, public asset reads, signed downloads, and frontend apps that need to read ETag after upload.
CORS generator
Use the generator when setting up a new bucket for public reads, signed downloads, browser uploads, or presigned URL uploads. The output includes CORS JSON, a provider command for wrangler R2 or aws s3api put-bucket-cors, and a preflight curl you can run with an Origin header. The generated CORS JSON is editable in Monaco, so you can start from a preset and then adjust multiple rules, wildcard headers, or provider-specific values before copying.
CORS debugger
Use the debugger after seeing errors like No Access-Control-Allow-Origin header, preflight request does not pass access control check, or Request header field Content-Type is not allowed. It identifies the missing origin, method, request header, or exposed response header and generates a corrected config. Paste the exact request origin from DevTools, the method used by your upload or download, the request headers sent by your code, and the browser error text. The tool also reads header names from common preflight error strings.
Presigned URL uploads
Presigned uploads often fail because the app sends headers that are not in AllowedHeaders. Common examples are Content-Type, x-amz-acl, and metadata headers. If the frontend needs the uploaded object's ETag, add it to ExposeHeaders.
CORS field reference
| Field | What it controls | Common values |
|---|---|---|
| AllowedOrigins | Which frontend origins may access the bucket from a browser. | https://app.example.com, http://localhost:3000, * |
| AllowedMethods | Which browser methods are allowed after preflight. | GET, HEAD, PUT, POST, DELETE |
| AllowedHeaders | Which request headers can be sent by upload/download code. | Content-Type, Authorization, x-amz-*, * |
| ExposeHeaders | Which response headers JavaScript can read. | ETag, Content-Length, Content-Type |
| MaxAgeSeconds | How long the browser can cache a successful preflight result. | 3600, 86400 |
Use-case presets
| Scenario | Methods | Headers to check |
|---|---|---|
| S3 presigned PUT upload | PUT, HEAD | Content-Type, x-amz-acl, x-amz-meta-* |
| R2 public image or video read | GET, HEAD | Usually none; expose Content-Length if the app reads it. |
| Signed download URL | GET, HEAD | Authorization only if your request sends it separately. |
| Browser form upload | POST, HEAD | Content-Type and any policy-related x-amz-* headers. |
Provider notes
- AWS S3 accepts a bucket CORS configuration with
CORSRulesin the CLI wrapper, while the console-style rule list usesAllowedOrigins,AllowedMethods, and related fields. - Cloudflare R2 uses the same human-readable rule field names in dashboard CORS JSON. The tool also understands Cloudflare API-style rule objects when debugging pasted responses.
- Local development needs explicit origins such as
http://localhost:3000. A production HTTPS origin does not cover localhost.
Common fixes
| Symptom | Likely fix |
|---|---|
| Missing Access-Control-Allow-Origin | Add the exact frontend origin to AllowedOrigins. |
| PUT upload preflight fails | Add PUT and any upload headers to the same matching rule. |
| Content-Type header blocked | Add Content-Type to AllowedHeaders. |
| x-amz-meta-* header blocked | Add the exact metadata header or a narrow x-amz-* wildcard. |
| Works in curl, fails in browser | Check preflight OPTIONS, origin, and Access-Control-Request-Headers. |
| Frontend cannot read ETag | Add ETag to ExposeHeaders. |
Debugging checklist
- Copy the exact frontend origin from DevTools, including protocol and port.
- Check whether the failing request is an OPTIONS preflight, the actual PUT/POST, or a GET after upload.
- Compare Access-Control-Request-Method with AllowedMethods.
- Compare Access-Control-Request-Headers with AllowedHeaders, case-insensitively.
- Add ETag or Content-Length to ExposeHeaders only when browser JavaScript needs to read those response headers.
- Avoid wildcard origins for credentialed requests. Use the exact app origin instead.
FAQ
Does this send requests to my bucket?
No. It analyzes pasted config and request details in the browser. Use the generated preflight curl if you want to test a live bucket.
Is R2 CORS different from S3 CORS?
For dashboard bucket CORS, Cloudflare R2 uses the same JSON field names developers commonly use with S3: AllowedOrigins, AllowedMethods, AllowedHeaders, ExposeHeaders, and MaxAgeSeconds.
Should I use AllowedOrigins wildcard?
Wildcard is convenient for public read assets, but exact origins are better for app uploads, signed URLs, and any credentialed request.
Similar tools
Pair this with the curl Viewer when you need to inspect the request headers your app is actually sending.