Skip to content

R2 CORS: AllowedOrigins accepts partial wildcards, but the docs say values must match exactly #32888

Description

@InfinityRocker

Existing documentation URL(s)

What changes are you suggesting?

The page states that AllowedOrigins entries must be a valid Origin header
value (scheme://host[:port]) and that "Origin values must match exactly".
It does not mention wildcards.

In practice, the R2 S3-compatible API (PutBucketCors) accepts partial
wildcards in AllowedOrigins and matches them at request time
. This is easy to
rely on by accident, and equally easy to have quietly break, because nothing in
the docs says whether it is supported.

Tested against a real bucket via PutBucketCors / GetBucketCors:

AllowedOrigin value Result
https://*-myapp.<subdomain>.workers.dev Accepted, and matches as expected
https://*.<subdomain>.workers.dev Accepted
http://localhost Accepted (exact origin, port 80)
http://localhost:* Rejected400 MalformedXML

For the first entry, the matching behaves correctly rather than degrading into
"allow all". With only that wildcard added:

  • https://a1b2c3d4-myapp.<subdomain>.workers.dev → receives
    Access-Control-Allow-Origin
  • https://other-worker.<subdomain>.workers.dev → no CORS header
  • https://myapp.<subdomain>.workers.dev.example.com (fake suffix) → no CORS header

So the feature appears to work deliberately, not incidentally.

The request is to make the docs and the API agree, in whichever direction is
correct:

  1. If partial wildcards are supported, document the syntax: which positions
    a * may appear in, whether it may span dots, and that a port wildcard
    (http://localhost:*) is not accepted.
  2. If they are not supported, say so explicitly, and ideally have
    PutBucketCors reject them the way it rejects http://localhost:*.
    Accepting a value and matching it, while documenting exact-match-only, is the
    worst of both: users build on it without knowing it is unsupported.

Either way, please state the port case directly. Allowing a local dev server on
an arbitrary port is a very common need, and today the only way to find out that
http://localhost:* is impossible is to try it and read a MalformedXML
response, which does not suggest the actual cause.

Additional information

The behaviour was found while debugging a browser upload that worked from one
deployment and failed from another with Failed to fetch; the cause was the
second origin missing from the bucket's CORS policy.

One note that may be worth a line in the page regardless of the wildcard
question: responses served from the *.r2.dev public URL are cached at the
edge, so immediately after changing a CORS policy, GETs can still return the
previously cached response without the Access-Control-Allow-Origin header
.
This makes a correct change look like it partially failed. Adding a cache-busting
query parameter shows the new policy right away.

Metadata

Metadata

Labels

product:r2R2 object storage: https://developers.cloudflare.com/r2

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions