Back

Blog details

What Is CORS and Why It Matters for Object Storage

AIOZ Network
6 min readSeptember 10, 2026
aioz-storage
Close-up of code displayed on a computer screen

What is CORS, and why does it show up constantly the moment a web app tries to talk to object storage directly from the browser? AWS's own documentation defines it precisely: cross-origin resource sharing "defines a way for client web applications that are loaded in one domain to interact with resources in a different domain." Without it, a browser blocks exactly the kind of request a modern web app needs to make routinely, JavaScript on one domain uploading a file straight to a storage bucket living on another. This article covers what CORS actually is, what a browser does before it even sends the real request, and what a working configuration is built from.

TL;DR:

  • CORS is a browser security rule, not a server-side one. It exists to stop JavaScript on one domain from freely reading responses from a different domain unless that domain explicitly allows it.
  • Before certain cross-origin requests go through, the browser sends a "preflight" OPTIONS request first, and only proceeds with the real request if the response says the origin, method, and headers are allowed.
  • A CORS configuration is a set of rules, allowed origins, allowed methods, allowed headers, and optional response-header exposure and cache duration, that a bucket owner sets explicitly. Nothing is allowed cross-origin by default.

What Is CORS?

CORS solves a problem browsers create on purpose. By default, a browser enforces the same-origin policy, JavaScript running on app.example.com isn't allowed to read a response from storage.example.com even if the request itself succeeds, purely because the two domains don't match. CORS is the mechanism that lets a server on the second domain explicitly say "requests from this specific origin are allowed," overriding that default block for exactly the origins, methods, and headers it names.

Why Object Storage Specifically Runs Into This

AWS's own documentation gives the exact scenario that makes this unavoidable for object storage: a website hosted in one S3 endpoint wanting to make "authenticated GET and PUT requests against the same bucket by using the Amazon S3 API endpoint," which is technically a different origin even though it's the same bucket. "A browser would normally block JavaScript from allowing those requests," AWS states directly, "but with CORS you can configure your bucket to explicitly enable cross-origin requests." Any web app that uploads directly to a bucket, rather than routing every file through its own backend server first, runs into this exact wall unless CORS is configured on the bucket itself.

The Preflight Request: What Actually Happens First

For anything beyond a simple GET, the browser doesn't just send the real request and hope for the best. It sends a preflight request first, an OPTIONS call asking the server, in effect, "would you actually allow this request if I sent it?" AWS's documentation describes exactly how a bucket evaluates one: the Origin header on the request has to match an entry in AllowedOrigins, the intended method has to match AllowedMethods, and any custom headers have to match AllowedHeaders. Only if all three match does the browser proceed with the actual request; a mismatch anywhere in that check fails the entire operation before a single byte of the real request goes out.

Server room aisle with rows of dark server racks

What a CORS Configuration Is Actually Made Of

A CORS configuration is a set of explicit rules, and AWS's own documentation defines exactly five elements that make one up. AllowedOrigins lists which domains can make cross-origin requests at all, a single wildcard is allowed, letting you scope it to https://.example.com rather than an exact match, or to every origin at once if that's genuinely intended. AllowedMethods is restricted to a fixed list, GET, PUT, POST, DELETE, and HEAD, nothing else is a valid CORS method. AllowedHeaders controls which custom headers a preflight request is allowed to ask for. Two optional elements round it out: ExposeHeaders, which lists response headers a browser's JavaScript is actually allowed to read (some headers arrive in the response either way but stay invisible to XMLHttpRequest unless explicitly exposed), and MaxAgeSeconds, which lets the browser cache a preflight result so it doesn't have to repeat that round trip on every single request to the same resource.

A Real Example: Direct Browser Uploads

The most common reason to configure CORS on an object storage bucket at all is letting a browser upload directly to it using a presigned URL, rather than routing every file through an application server first. That's a real, meaningful architecture choice, it takes load off your own backend and lets large files go straight to storage, but it only works if the bucket's CORS configuration explicitly allows the origin your web app is served from, the PUT (or POST) method the upload actually uses, and any headers your upload code sends along with it. Skip any one of those three, and the browser blocks the upload before it ever reaches the bucket, regardless of whether the presigned URL itself is valid.

Where AIOZ Storage Fits

AIOZ Storage's own documentation doesn't describe CORS configuration anywhere, checked directly against its concept pages rather than assumed either way. That's a real, honest gap worth stating plainly rather than guessing at: since AIOZ Storage is S3-compatible at the API level, whether bucket-level CORS rules can be set the same way S3's PutBucketCors API allows is genuinely unconfirmed, not something this article can responsibly claim works just because the underlying protocol theoretically supports it elsewhere. Anyone planning a direct-to-browser upload architecture on AIOZ Storage specifically should treat this as an open question to test directly, not an assumed yes.

Frequently Asked Questions

What is CORS in simple terms?
A browser security rule that blocks JavaScript on one domain from reading responses from a different domain, unless that domain explicitly allows it through a CORS configuration. It's enforced by the browser, not the server sending the response.

Why do object storage buckets need CORS configured?
Because a web app's own domain and its storage bucket's API endpoint are usually different origins, even when they're closely related. Any browser-side JavaScript making direct requests to the bucket, uploads especially, gets blocked without an explicit CORS configuration allowing it.

What is a preflight request?
An OPTIONS request the browser sends before certain cross-origin requests, asking the server whether the actual request's origin, method, and headers would be allowed. The real request only proceeds if the preflight response confirms all three.

What methods can a CORS rule allow?
GET, PUT, POST, DELETE, and HEAD, per AWS's own specification. No other HTTP methods are valid values for a CORS AllowedMethods element.

What does ExposeHeaders actually do?
It lists which response headers a browser's JavaScript is allowed to read directly. Some headers still arrive in the response either way, they're just invisible to code like XMLHttpRequest unless the CORS configuration explicitly exposes them.

Does presigned-URL upload still need CORS if the URL itself is valid?
Yes. A valid presigned URL only proves the request is authorized, it says nothing about whether the browser is allowed to make that cross-origin request in the first place. Both checks have to pass independently.

Does AIOZ Storage support CORS configuration?
Not documented. AIOZ Storage's own documentation doesn't describe CORS configuration anywhere, so whether bucket-level CORS rules work the same way they do on AWS S3 is unconfirmed, worth testing directly rather than assuming.

References

We only send updates when meaningful changes ship, and you can unsubscribe anytime

Related Content

blog thumbnail

How to Build an MCP Server for AIOZ Storage, Step by Step

AIOZ Storage has no official MCP server yet. Here is a working MCP server for AIOZ Storage, built with the official Python SDK and boto3, tools included.

aioz-storage
6 min readSeptember 19, 2026
blog thumbnail

What Is an MCP Server? How AI Agents Access Storage

What is an MCP server: a standard letting AI agents like Claude use tools and data through one protocol. How MinIO and Azure apply it to object storage.

aioz-storage
6 min readSeptember 16, 2026
blog thumbnail

S3 Access Logs vs CloudTrail: Which Should You Use?

S3 access logs vs CloudTrail: AWS recommends CloudTrail, but each one catches real events the other misses. Speed, cost, and coverage, compared directly.

aioz-storage
5 min readSeptember 15, 2026
blog thumbnail

Eventual vs Strong Consistency: How S3 Made the Switch

Eventual vs strong consistency: whether a read right after a write sees the new data immediately. S3 ran on the first model for 14 years, then changed it.

aioz-storage
6 min readSeptember 14, 2026
blog thumbnail

Bucket Policy vs IAM Policy: What Is the Real Difference?

Bucket policy vs IAM policy: one attaches to the resource, one attaches to the identity. What each can do that the other can't, and how S3 evaluates both.

aioz-storage
6 min readSeptember 13, 2026
blog thumbnail

SSE-S3 vs SSE-KMS vs SSE-C: S3 Encryption Types Explained

SSE-S3 vs SSE-KMS vs SSE-C: who manages the encryption key, and what that decision actually costs you. Includes the real 2026 default change to SSE-C.

aioz-storage
6 min readSeptember 12, 2026