Amazon S3 Integration
Amazon S3 (Simple Storage Service) is a scalable object storage service provided by AWS. Sveltia CMS supports S3 as a media storage backend with direct browser-to-S3 uploads using AWS Signature Version 4 — no backend proxy is required.
Requirements
- An AWS account with an S3 bucket created.
- An IAM user with an access key and the minimum required permissions (see Credentials below).
CSP
If your site uses a Content Security Policy (CSP), you need to allow the S3 endpoint. See Content Security Policy below for details.
Setup
Credentials
Create an IAM user with programmatic access and attach a policy granting the minimum required permissions:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:ListBucket", "s3:GetObject", "s3:PutObject", "s3:DeleteObject"],
"Resource": ["arn:aws:s3:::my-bucket", "arn:aws:s3:::my-bucket/*"]
}
]
}The s3:DeleteObject action is only needed to delete and rename files and folders from the Asset Library. Leave it out if you don’t want editors to remove files. Renaming copies the object to the new key and then deletes the original. An empty folder is kept with a zero-byte placeholder object.
The resulting Access Key ID goes in access_key_id in your config. The Secret Access Key is entered by users in the CMS UI when they access the media library for the first time — it is never stored in config.
Public Read Access
Asset preview and download URLs are unsigned direct storage URLs, so objects must be publicly readable. Sveltia CMS doesn’t set object ACLs, which new buckets have disabled by default, so grant public read access with a bucket policy instead:
- In the S3 console, open the bucket’s Permissions tab.
- Under Block Public Access, disable
BlockPublicPolicyandRestrictPublicBuckets. - Add a bucket policy granting
s3:GetObjectto"Principal": "*"for the objects path:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-bucket/cms-uploads/*"
}
]
}CORS
Configure cross-origin resource sharing in the S3 console under Bucket > Permissions > Cross-origin resource sharing (CORS). CORS is required because Sveltia CMS sends custom AWS Signature v4 headers that trigger a preflight request.
[
{
"AllowedHeaders": ["*"],
"AllowedMethods": ["GET", "PUT", "DELETE", "HEAD"],
"AllowedOrigins": ["https://your-cms-domain.com"],
"ExposeHeaders": ["ETag"],
"MaxAgeSeconds": 3000
}
]The DELETE method and the wildcard AllowedHeaders are needed to delete and rename files from the Asset Library. If you configured CORS before these operations became available, add them to your existing policy. Otherwise, the browser will block those requests during the preflight check.
Bucket Naming
For virtual-hosted-style URLs (the default), the bucket name must be DNS-compatible: lowercase letters, numbers and hyphens only. If your bucket name contains underscores, use force_path_style: true in your config.
Configuration
Here’s an example configuration for Amazon S3:
media_libraries:
aws_s3:
access_key_id: AKIAIOSFODNN7EXAMPLE
bucket: my-website-assets
region: us-east-1
prefix: cms-uploads/ # Optional
force_path_style: false # Optional, see Bucket Naming above
public_url: https://media.example.com # Optional, see Custom Domain below[media_libraries.aws_s3]
access_key_id = "AKIAIOSFODNN7EXAMPLE"
bucket = "my-website-assets"
region = "us-east-1"
prefix = "cms-uploads/"
force_path_style = false
public_url = "https://media.example.com"{
"media_libraries": {
"aws_s3": {
"access_key_id": "AKIAIOSFODNN7EXAMPLE",
"bucket": "my-website-assets",
"region": "us-east-1",
"prefix": "cms-uploads/",
"force_path_style": false,
"public_url": "https://media.example.com"
}
}
}{
media_libraries: {
aws_s3: {
access_key_id: 'AKIAIOSFODNN7EXAMPLE',
bucket: 'my-website-assets',
region: 'us-east-1',
prefix: 'cms-uploads/', // Optional
force_path_style: false, // Optional
public_url: 'https://media.example.com', // Optional
},
},
}WARNING
Do not write your Secret Access Key in the configuration file, as it should be kept confidential and not exposed in client-side code. Users will be prompted to enter the key when they use the storage for the first time. It’s then stored securely in the browser’s local storage.
Configuration Properties
| Property | Required | Description |
|---|---|---|
access_key_id | Yes | AWS Access Key ID (safe to store in config). |
bucket | Yes | The S3 bucket name. |
region | Yes | AWS region, e.g. us-east-1, eu-west-1. With a custom endpoint, the region of your server; see Self-Hosted Storage below. |
prefix | No | Path prefix within the bucket, e.g. uploads/. A trailing slash is added if missing. |
force_path_style | No | Use path-style URLs (s3.region.amazonaws.com/bucket) instead of virtual-hosted-style (bucket.s3.region.amazonaws.com). Defaults to false. Has no effect with endpoint, which always uses path-style URLs. |
endpoint | No | Custom endpoint URL for another S3-compatible service, such as Garage or MinIO, e.g. https://s3.example.com. Objects are addressed as {endpoint}/{bucket}/{key}. See Self-Hosted Storage below. |
public_url | No | Custom domain for asset URLs (e.g. a CloudFront distribution). See Custom Domain below. |
Custom Domain
If you serve assets through a CloudFront distribution or a Route 53 custom domain pointing to your S3 bucket, set public_url to that URL. Asset preview and download URLs in the CMS will use the custom domain instead of the S3 endpoint:
public_url: 'https://media.example.com'The S3 API endpoint is still used for listing and uploading — only the asset URLs shown in the CMS change. See the Route 53 getting-started guide for setting up a custom domain with S3.
Self-Hosted Storage
The Amazon S3 integration also works with a self-hosted, S3-compatible object storage server, such as Garage or MinIO, for organizations that must keep their assets on their own infrastructure. Set endpoint to the URL of the server’s S3 API:
media_libraries:
aws_s3:
endpoint: https://s3.example.com
region: garage
access_key_id: GK31c2f218a2e44f485b94239e
bucket: my-website-assets
public_url: https://my-website-assets.web.example.com # Optional, see below[media_libraries.aws_s3]
endpoint = "https://s3.example.com"
region = "garage"
access_key_id = "GK31c2f218a2e44f485b94239e"
bucket = "my-website-assets"
public_url = "https://my-website-assets.web.example.com"{
"media_libraries": {
"aws_s3": {
"endpoint": "https://s3.example.com",
"region": "garage",
"access_key_id": "GK31c2f218a2e44f485b94239e",
"bucket": "my-website-assets",
"public_url": "https://my-website-assets.web.example.com"
}
}
}{
media_libraries: {
aws_s3: {
endpoint: 'https://s3.example.com',
region: 'garage',
access_key_id: 'GK31c2f218a2e44f485b94239e',
bucket: 'my-website-assets',
public_url: 'https://my-website-assets.web.example.com', // Optional, see below
},
},
}Keep the following in mind:
- Path-style URLs: With a custom
endpoint, objects are always addressed as{endpoint}/{bucket}/{key}, so the server doesn’t need a wildcard DNS record or a TLS certificate for bucket subdomains.force_path_stylehas no effect. - Region: The region isn’t part of the URL, but it’s used to sign every request. It must therefore match the region the server is configured with. For Garage, that’s the
s3_regionoption (garagein the sample configuration). For MinIO, it’sus-east-1unless you’ve changed it. A mismatch makes every request fail with a signature error. - CORS: The server must allow cross-origin requests from the CMS origin, as described in CORS above. With Garage, apply the same policy to the bucket through its S3 API, e.g. with
aws s3api put-bucket-cors --endpoint-url https://s3.example.com. MinIO allows any origin by default. If you’ve restricted it, add the CMS origin to itscors_allow_originsetting. - Public read access: Asset preview and download URLs are unsigned, so objects must be readable without credentials. On MinIO, allow anonymous downloads on the bucket, e.g. with
mc anonymous set download. Garage doesn’t serve anonymous requests on its S3 API. Instead, enable website access on the bucket and setpublic_urlto the bucket’s web endpoint or a reverse proxy in front of it. - HTTPS: If the CMS is served over HTTPS, browsers block requests to a plain
http://endpoint as mixed content, except forlocalhost. Put the server behind a reverse proxy with a TLS certificate. - Content Security Policy: Add the
endpointorigin, and thepublic_urlorigin if any, toconnect-srcandimg-src. See Content Security Policy below.
Content Security Policy
The URL pattern depends on whether force_path_style is enabled.
Virtual-hosted-style (default) — https://{bucket}.s3.{region}.amazonaws.com:
connect-src https://*.s3.us-east-1.amazonaws.com;
img-src https://*.s3.us-east-1.amazonaws.com;Path-style (force_path_style: true) — https://s3.{region}.amazonaws.com/{bucket}:
connect-src https://s3.us-east-1.amazonaws.com;
img-src https://s3.us-east-1.amazonaws.com;Replace us-east-1 with your actual bucket region.
If using a custom domain via public_url, add it to connect-src and img-src as well:
connect-src https://*.s3.us-east-1.amazonaws.com
https://media.example.com;
img-src https://*.s3.us-east-1.amazonaws.com
https://media.example.com;The asset host must be in connect-src because the Download and Copy buttons and the text preview in the Asset Library fetch the file from its public URL. For the same reason, a custom domain must also send CORS headers that allow the CMS origin. Otherwise, downloads fail.
See the CSP documentation for more details.
Accessing the Storage
There are two ways to use Amazon S3 in Sveltia CMS:
File and Image Fields
When editing content entries, users can use File and Image fields to upload and select media on Amazon S3 directly within the entry editor. After entering the Secret Access Key in the CMS UI when prompted, users can upload new media directly to S3 or select existing media from the bucket.
When uploading media, files will be stored in the S3 bucket, and the CMS can take advantage of S3’s capabilities directly from the CMS. You can also select existing media from your S3 storage.
Asset Library
Amazon S3 also appears under External Locations in the Asset Library. There, users can browse, search, sort and filter the files in your bucket, and upload, rename, replace, download or delete them without leaving the CMS.
The files and folders are browsed folder by folder:
- Subfolders are listed ahead of the files.
- Uploads go to the folder being viewed.
- Folders can be created, renamed and deleted.