Skip to content

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:

json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["s3:ListBucket", "s3:GetObject", "s3:PutObject"],
      "Resource": ["arn:aws:s3:::my-bucket", "arn:aws:s3:::my-bucket/*"]
    }
  ]
}

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:

  1. In the S3 console, open the bucket's Permissions tab.
  2. Under Block Public Access, disable BlockPublicPolicy and RestrictPublicBuckets.
  3. Add a bucket policy granting s3:GetObject to "Principal": "*" for the objects path:
json
{
  "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.

json
[
  {
    "AllowedHeaders": ["*"],
    "AllowedMethods": ["GET", "PUT", "HEAD"],
    "AllowedOrigins": ["https://your-cms-domain.com"],
    "ExposeHeaders": ["ETag"],
    "MaxAgeSeconds": 3000
  }
]

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:

yaml
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
toml
[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"
json
{
  "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"
    }
  }
}
js
{
  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 first time, which will be stored securely in the browser’s local storage.

Configuration Properties

PropertyRequiredDescription
access_key_idYesAWS Access Key ID (safe to store in config).
bucketYesThe S3 bucket name.
regionYesAWS region, e.g. us-east-1, eu-west-1.
prefixNoPath prefix within the bucket, e.g. uploads/.
force_path_styleNoUse path-style URLs (s3.region.amazonaws.com/bucket) instead of virtual-hosted-style (bucket.s3.region.amazonaws.com). Defaults to false.
public_urlNoCustom 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:

yaml
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.

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 img-src as well:

connect-src https://*.s3.us-east-1.amazonaws.com;
img-src     https://*.s3.us-east-1.amazonaws.com
            https://media.example.com;

See the CSP documentation for more details.

Accessing the Storage

The Amazon S3 media storage can be accessed through the File and Image fields in Sveltia CMS. Enter your Secret Access Key in the CMS UI when prompted, and you’ll be able to upload new media directly to S3 or select existing media from your bucket.

When uploading media, files will be stored in your S3 bucket, and you can take advantage of S3’s capabilities directly from the CMS. You can also select existing media from your S3 storage.

Future Plans

You’ll be able to manage your S3 files directly from the Asset Library in future releases.

Released under the MIT License.