# GetBucketCors

GetBucketCors returns a bucket's CORS configuration - every rule, as an XML `CORSConfiguration`
document. Rules saved in the dashboard and rules set with [PutBucketCors](https://docs.strg.com/s3-api/PutBucketCors) are
the same rules, so this operation returns both. [CORS rules](https://docs.strg.com/cors-rules) explains what the
rules do and how a browser uses them.

**Info: Good to know on STRG**

- Rules come back in a tidy, consistent form: without an `ID`, with origins and header names
  lowercased, and with methods possibly in a different order from the one they were sent in.
- Every access key can read the CORS rules of the buckets it reaches - no extra permission
  needed.
- `x-amz-expected-bucket-owner` is welcome and has nothing to check.

## When you'd use it

- A deploy script is about to add a new origin, say a staging site. PutBucketCors replaces every
  rule at once, so the script reads the current rules first, adds the new origin and writes the
  full set back.
- A browser upload starts failing with a CORS error. Reading the bucket's rules shows straight
  away whether the page's origin, method and headers are on the list.
- An agent looking after several buckets checks which websites can reach each one from a browser,
  and reports any bucket whose rules allow every origin.
- A team keeps its bucket configuration in version control, and a nightly job compares the rules
  in the repository with the ones the bucket actually has.

## Request

```http
GET /?cors HTTP/1.1
Host: my-bucket.s3.eu-central-1.strg.com
Authorization: AWS4-HMAC-SHA256 Credential=...
x-amz-date: 20260926T120000Z
x-amz-content-sha256: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
```

Path-style, the same request is `GET /my-bucket?cors` on `s3.eu-central-1.strg.com`.
[Addressing](https://docs.strg.com/s3-api/overview#addressing) describes both styles.

### Permissions

Every access key can call this: **Read bucket settings** (`buckets:read-config`) is always
included. A key limited to certain buckets reads the rules of those buckets, and gets
`403 AccessDenied` for any other. [Access keys](https://docs.strg.com/access-keys) has the details.

### URI parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `Bucket` | String | Yes | The bucket whose CORS configuration is returned. |
| `cors` | Flag | Yes | Selects the CORS subresource. Sent without a value. |

STRG answers any other query parameter with `501 NotImplemented`, so a client knows straight
away that the request was not what it meant.

### Request headers

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `x-amz-expected-bucket-owner` | String | No | Welcome, and has nothing to check: an access key only ever reaches its own account's buckets. |

### Request body

None.

## Response

A bucket with CORS rules answers `200 OK` with the configuration. A bucket without any answers
`404 NoSuchCORSConfiguration`, which the AWS SDKs recognise by that code.

### Response headers

| Name | Description |
| --- | --- |
| `Content-Type` | `application/xml`. |
| `x-amz-request-id` | The request ID, for support requests. |

### Response body

The document holds one `CORSRule` element per rule, each made up of the elements below.

| Element | Type | Description |
| --- | --- | --- |
| `CORSConfiguration` | Container | The root element. |
| `CORSRule` | Container | One rule, repeated for each. At most 100. |
| `AllowedOrigin` | String | An origin the rule allows, lowercased. One element per origin. |
| `AllowedMethod` | String | A method the rule allows: `GET`, `HEAD`, `PUT`, `POST` or `DELETE`. One element per method, in no guaranteed order. |
| `AllowedHeader` | String | A request header the rule allows, lowercased. One element per header; absent if the rule allows none. |
| `ExposeHeader` | String | A response header browsers may expose to scripts, lowercased. One element per header; absent if the rule names none. |
| `MaxAgeSeconds` | Integer | How long a browser may cache the preflight answer, in seconds. Absent if the rule sets none. |
| `ID` | String | Never returned: STRG sets rule IDs aside when the rules are saved. |

```xml
<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
  <CORSRule>
    <AllowedOrigin>https://www.example.com</AllowedOrigin>
    <AllowedMethod>GET</AllowedMethod>
    <AllowedMethod>PUT</AllowedMethod>
    <AllowedHeader>*</AllowedHeader>
    <ExposeHeader>etag</ExposeHeader>
    <MaxAgeSeconds>3600</MaxAgeSeconds>
  </CORSRule>
</CORSConfiguration>
```

## Errors

These are the errors this operation can answer, and the situation behind each.

| Code | HTTP status | When |
| --- | --- | --- |
| `NoSuchCORSConfiguration` | 404 | The bucket has no CORS rules. |
| `NoSuchBucket` | 404 | The bucket does not exist, it is in a region other than the endpoint's, or your account has no storage in this endpoint's region yet. |
| `AccessDenied` | 403 | The access key is limited to buckets that do not include this one, or your account is not active. |
| `InvalidAccessKeyId` | 403 | The access key does not exist, has expired or been revoked, or was created so recently that it is not yet usable in this endpoint's region - a moment later it is. |
| `SignatureDoesNotMatch` | 403 | The request signature is wrong. |
| `RequestTimeTooSkewed` | 403 | The request time is more than 15 minutes from the server's. |
| `AuthorizationHeaderMalformed` | 400 | The signature's credential scope names a region other than the endpoint's. |
| `NotImplemented` | 501 | The request carries a query parameter this operation does not take. |
| `SlowDown` | 503 | Too many requests at once; a retry after the `Retry-After` interval goes through. |
| `ServiceUnavailable` | 503 | A temporary failure; a retry usually succeeds. |

## Differences from Amazon S3

A few things work a little differently on STRG, each listed here with what it means for your
requests.

| Aspect | Amazon S3 | STRG |
| --- | --- | --- |
| Rule `ID` | Returned as stored | Not returned: set aside when the rules are saved |
| Case of origins and headers | As sent | Lowercased |
| Order of `AllowedMethod` | As sent | Not guaranteed, so comparing methods as a set is the reliable check |
| Permission | `s3:GetBucketCORS`, granted per identity | Included in every access key that can reach the bucket |
| Bucket in another region | `301` redirect with `x-amz-bucket-region` | `404 NoSuchBucket`. Each bucket answers on its own regional endpoint. |
| `x-amz-expected-bucket-owner` | Checked against the bucket owner | Welcome, with nothing to check: a key only ever reaches buckets of its own account |

The rules read here are the ones the S3 endpoint applies to browser requests for the bucket: a
preflight they do not allow gets `403 AccessForbidden`, and responses to an origin they allow
carry the CORS headers. [CORS rules](https://docs.strg.com/cors-rules#what-a-browser-gets) describes what a
browser sees.

## Examples

Each example reads the CORS rules of `my-bucket`, and treats a bucket without rules as an empty
list.

**AWS CLI**

```bash
aws s3api get-bucket-cors \
  --bucket my-bucket \
  --endpoint-url https://s3.eu-central-1.strg.com \
  --region eu-central-1
```

The CLI prints the rules as JSON, under `CORSRules`. That list is also the format the dashboard's
CORS editor takes, so it can be pasted straight in. For a bucket without rules, the CLI ends with
`An error occurred (NoSuchCORSConfiguration)` and a non-zero exit code.

**Python (boto3)**

```python
import boto3
from botocore.exceptions import ClientError

s3 = boto3.client(
    "s3",
    endpoint_url="https://s3.eu-central-1.strg.com",
    region_name="eu-central-1",
)

try:
    rules = s3.get_bucket_cors(Bucket="my-bucket")["CORSRules"]
except ClientError as error:
    if error.response["Error"]["Code"] != "NoSuchCORSConfiguration":
        raise
    rules = []

for rule in rules:
    print(rule["AllowedOrigins"], rule["AllowedMethods"])
```

**JavaScript (AWS SDK v3)**

```javascript
import { GetBucketCorsCommand, S3Client } from "@aws-sdk/client-s3";

const client = new S3Client({
  endpoint: "https://s3.eu-central-1.strg.com",
  region: "eu-central-1",
});

let rules = [];
try {
  const output = await client.send(new GetBucketCorsCommand({ Bucket: "my-bucket" }));
  rules = output.CORSRules ?? [];
} catch (error) {
  if (error.name !== "NoSuchCORSConfiguration") throw error;
}

for (const rule of rules) {
  console.log(rule.AllowedOrigins, rule.AllowedMethods);
}
```

## Related

- [PutBucketCors](https://docs.strg.com/s3-api/PutBucketCors)
- [DeleteBucketCors](https://docs.strg.com/s3-api/DeleteBucketCors)
- [CORS rules](https://docs.strg.com/cors-rules)
- [Access keys](https://docs.strg.com/access-keys)
- [S3 API overview](https://docs.strg.com/s3-api/overview)
