# Amazon S3 (https://v2.s3.dimah.dev/docs/providers/aws-s3)

Amazon S3 supports presigned PUT and POST uploads, object ACLs, SHA-256
checksums, and multipart uploads.

## 1. Bucket CORS configuration [#1-bucket-cors-configuration]

Because the browser uploads directly to your S3 bucket, configure a bucket
CORS policy:

```json
[
  {
    "AllowedOrigins": ["https://your-app.example", "http://localhost:3000"],
    "AllowedMethods": ["GET", "PUT", "POST", "HEAD"],
    "AllowedHeaders": ["*"],
    "ExposeHeaders": ["ETag", "Content-Type"],
    "MaxAgeSeconds": 3000
  }
]
```

## 2. Server configuration [#2-server-configuration]

```ts title="lib/s3.ts"
import { S3Client } from "@aws-sdk/client-s3";
import { dimahS3, route } from "@dimah-s3/server";

export const awsS3 = new S3Client({
  region: process.env.S3_REGION!,
  credentials: {
    accessKeyId: process.env.S3_ACCESS_KEY_ID!,
    secretAccessKey: process.env.S3_SECRET_ACCESS_KEY!,
  },
  requestChecksumCalculation: "WHEN_REQUIRED", // [!code ++]
});

export const s3 = dimahS3({
  client: awsS3,
  bucket: process.env.S3_BUCKET!,
  routes: {
    avatar: route({
      upload: {
        fileTypes: ["image/*"],
        maxFileSize: 2 * 1024 * 1024,
      },
    }),
  },
});
```

AWS SDK versions from 3.729 default `requestChecksumCalculation` to
`"WHEN_SUPPORTED"`. Presigned browser uploads cannot match the checksum that
mode adds, so dimah-s3 refuses to presign with such a client.

## Frequently asked questions [#frequently-asked-questions]

<Accordions>

<Accordion title="How do I make uploaded files publicly accessible?">

If your bucket allows object ACLs, set `upload: { acl: "public-read" }` on the
route:

```ts
avatar: route({
  upload: {
    acl: "public-read", // [!code ++]
    fileTypes: ["image/*"],
    maxFileSize: 2 * 1024 * 1024,
  },
});
```

New S3 buckets commonly use **Bucket owner enforced** Object Ownership, which
disables ACLs. For those buckets, keep uploads private and grant public reads
through a bucket policy or CDN instead.

</Accordion>

<Accordion title="How do I troubleshoot a 403 or CORS error?">

First, make sure `AllowedOrigins` exactly matches the browser origin, including
the scheme and development port. Then allow the upload method and request
headers used by the presigned request. A presigned PUT sends metadata,
checksum, and tag headers, so `AllowedHeaders: ["*"]` is the simplest rule.
`ExposeHeaders` does not fix a 403, but it must include `ETag` when the
browser needs to read multipart responses.

A 403 with `SignatureDoesNotMatch` usually means the client changed or
dropped a header. A custom client must send exactly the `headers` from the
presign response.

</Accordion>

</Accordions>
