cloudflare / cloudflare/cloudflare-docs
R2 CORS: AllowedOrigins accepts partial wildcards, but the docs say values must match exactly
- Dominant language
- MDX
- Stars
- 5.2k
- Forks
- 16.7k
- Avg merge
- 2d 6h
- Merged PRs (30d)
- 337
Description
### Existing documentation URL(s)
- https://developers.cloudflare.com/r2/buckets/cors/
### 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..workers.dev` | **Accepted**, and matches as expected |
| `https://*..workers.dev` | **Accepted** |
| `http://localhost` | Accepted (exact origin, port 80) |
| `http://localhost:*` | **Rejected** — `400 MalformedXML` |
For the first entry, the matching behaves correctly rather than degrading into
"allow all". With only that wildcard added:
- `https://a1b2c3d4-myapp..workers.dev` → receives
`Access-Control-Allow-Origin`
- `https://other-worker..workers.dev` → no CORS header
- `https://myapp..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, `GET`s 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.
Contributor guide
Assessment
This issue has not been assessed yet.