---
url: /en/docs/media/backblaze-b2.md
description: >-
  Use Backblaze B2 as an external media storage provider in Sveltia CMS for
  cost-effective, S3-compatible object storage with zero egress fees.
---

# Backblaze B2 Integration

[Backblaze B2](https://www.backblaze.com/cloud-storage) is an S3-compatible object storage service with zero egress fees and low-cost storage. Sveltia CMS supports B2 as a media storage backend with direct browser-to-B2 uploads using AWS Signature Version 4 — no backend proxy is required.

## Requirements

* A Backblaze account with a B2 bucket created.
* A B2 Application Key (see [Credentials](#credentials) below).
* **CORS configuration via CLI** (see [CORS](#cors) below) — the web console doesn’t support the required S3 operations.

### CSP

If your site uses a Content Security Policy (CSP), you need to allow the B2 endpoint. See [Content Security Policy](#content-security-policy) below for details.

## Setup

### Credentials

B2 uses Application Keys for authentication. You can create keys via **Backblaze Console > Account > Application Keys**.

For production use, create a **bucket-scoped Application Key** with the following capabilities:

* `listFiles`
* `readFiles`
* `writeFiles`
* `deleteFiles`

Using the B2 CLI:

```sh
b2 key create --bucket my-bucket sveltia-cms listFiles,readFiles,writeFiles,deleteFiles
```

The resulting **keyID** goes in `access_key_id` in your config. The **applicationKey** (shown only once) 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

For asset previews and downloads to work, objects must be publicly readable. When creating a bucket, set its visibility to **Public**:

```sh
b2 bucket create my-bucket allPublic
```

::: info

Making a bucket public requires a payment method on your Backblaze account. Alternatively, you can use a private bucket with a custom `public_url` pointing to a CDN that provides public access.

:::

With a public bucket, B2’s S3 endpoint serves objects anonymously, so no separate `public_url` configuration is needed — asset URLs use the virtual-hosted storage endpoint directly (`https://{bucket}.s3.{region}.backblazeb2.com/{key}`).

### CORS

::: warning Important

The CORS configuration required by Sveltia CMS **cannot be set via the B2 web console**. The console only offers four basic presets that don’t include the S3 operations needed for uploads and listings. You must use the **B2 CLI**, native API, or AWS CLI to configure CORS.

This is documented, by-design behavior — see [Enable and Manage CORS Rules in the Enterprise Web Console](https://www.backblaze.com/docs/cloud-storage-enable-and-manage-cors-rules).

:::

CORS is required because Sveltia CMS sends custom AWS Signature v4 headers that trigger a preflight request.

#### Setting CORS via B2 CLI

**1. Install and authorize the B2 CLI** (use a key with `writeBuckets` capability):

See the [official documentation](https://www.backblaze.com/docs/cloud-storage-command-line-tools) for installation instructions.

```sh
b2 account authorize <adminKeyId> <adminKey>
```

**2. Set the CORS rule with S3 operations:**

```sh
b2 bucket update --cors-rules '[{
  "corsRuleName": "sveltia-cms",
  "allowedOrigins": ["https://your-cms-domain.com"],
  "allowedOperations": ["s3_get", "s3_head", "s3_put", "s3_delete", "s3_post"],
  "allowedHeaders": ["*"],
  "exposeHeaders": ["ETag", "x-amz-request-id"],
  "maxAgeSeconds": 3600
}]' my-bucket allPublic
```

Replace `https://your-cms-domain.com` with your actual CMS origin and `my-bucket` with your bucket name. A wildcard `*` is allowed for `allowedOrigins` if you want to allow all origins.

::: tip

You can also pass `--cors-rules` to `b2 bucket create` to set CORS when creating the bucket.

:::

See the [official documentation](https://www.backblaze.com/docs/cloud-storage-enable-cors-with-the-cli) for more details on CORS configuration.

**3. Verify the configuration** (optional):

```sh
b2 bucket get my-bucket
```

#### Required CORS Operations

The S3 operations Sveltia CMS requires are:

* `s3_get` — for `ListObjectsV2` (listing) and `GetObject` (preview/download)
* `s3_head` — for `HeadObject` (metadata checks)
* `s3_put` — for `PutObject` (uploads and replacements) and `CopyObject` (renames)
* `s3_delete` — for `DeleteObject` (deletions and renames)
* `s3_post` — for multipart uploads

Deleting and renaming files from the [Asset Library](/en/docs/ui/asset-library) require `s3_delete` and the wildcard `allowedHeaders`; if your existing rule lacks them, update it, otherwise the browser will block those requests during the preflight check.

B2 maps these native operations to the standard S3 HTTP methods (`GET`, `HEAD`, `PUT`, `DELETE`, `POST`).

## Configuration

Here’s an example configuration for Backblaze B2:

::: code-group

```yaml [YAML]
media_libraries:
  backblaze_b2:
    access_key_id: 0123456789abcdef0123456789ab
    bucket: my-bucket
    region: us-east-005
    prefix: cms-uploads/ # Optional
    public_url: https://cdn.example.com # Optional, only for private buckets
```

```toml [TOML]
[media_libraries.backblaze_b2]
access_key_id = "0123456789abcdef0123456789ab"
bucket = "my-bucket"
region = "us-east-005"
prefix = "cms-uploads/"
public_url = "https://cdn.example.com"
```

```json [JSON]
{
  "media_libraries": {
    "backblaze_b2": {
      "access_key_id": "0123456789abcdef0123456789ab",
      "bucket": "my-bucket",
      "region": "us-east-005",
      "prefix": "cms-uploads/",
      "public_url": "https://cdn.example.com"
    }
  }
}
```

```js [JavaScript]
{
  media_libraries: {
    backblaze_b2: {
      access_key_id: '0123456789abcdef0123456789ab',
      bucket: 'my-bucket',
      region: 'us-east-005',
      prefix: 'cms-uploads/', // Optional
      public_url: 'https://cdn.example.com', // Optional
    },
  },
}
```

:::

::: warning

Do not write your Application Key (secret) 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, which will be stored securely in the browser’s local storage.

:::

### Configuration Properties

| Property | Required | Description |
| --- | --- | --- |
| `access_key_id` | Yes | B2 Application Key ID (keyID). Safe to store in config. |
| `bucket` | Yes | The B2 bucket name. |
| `region` | Yes | B2 region, e.g. `us-west-001`, `us-east-005`, `eu-central-003`. See [B2 regions list](https://www.backblaze.com/docs/cloud-storage-regions-and-endpoints). |
| `prefix` | No | Path prefix within the bucket, e.g. `uploads/`. |
| `public_url` | No | CDN or custom domain URL for asset previews. Only required for **private buckets**. Public buckets serve objects directly via the S3 endpoint. |

## Public Buckets vs. Private Buckets

### Public Buckets (Recommended for Simplicity)

When a bucket is set to **Public** (`allPublic`), B2’s S3 endpoint serves objects anonymously without authentication. Asset URLs use the virtual-hosted storage endpoint:

```
https://{bucket}.s3.{region}.backblazeb2.com/{key}
```

No `public_url` configuration is needed — previews and downloads work automatically.

### Private Buckets (Requires CDN)

For private buckets, you must configure a `public_url` pointing to a CDN or proxy that provides public access to the objects. B2’s S3 API always requires authentication, so direct storage URLs won’t work for previews in the CMS.

```yaml
public_url: 'https://cdn.example.com'
```

::: warning

The storage endpoint is always used for listing and uploading operations regardless of the `public_url` setting. Only the asset preview/download URLs shown in the CMS change.

:::

## Content Security Policy

API calls (list, upload) go to `https://s3.{region}.backblazeb2.com`. For public buckets, asset URLs use `https://{bucket}.s3.{region}.backblazeb2.com`:

```
connect-src https://s3.us-east-005.backblazeb2.com;
img-src     https://my-bucket.s3.us-east-005.backblazeb2.com;
```

If using a private bucket with a CDN via `public_url`:

```
connect-src https://s3.us-east-005.backblazeb2.com;
img-src     https://cdn.example.com;
```

Replace `us-east-005` and `my-bucket` with your actual region and bucket name.

See the [CSP documentation](/en/docs/security#setting-up-content-security-policy) for more details.

## B2-Specific Notes

### No Per-Object ACLs

B2’s S3 API does not support the `x-amz-acl` header for per-object access control. Visibility is controlled at the bucket level (public or private). Sveltia CMS automatically omits the `x-amz-acl: public-read` header when uploading to B2, so uploads work correctly regardless of bucket visibility.

### Region Endpoint

B2 uses a region-specific endpoint for the S3 API (`https://s3.{region}.backblazeb2.com`), and asset URLs use virtual-hosted-style addressing (`https://{bucket}.s3.{region}.backblazeb2.com/{key}`) for public buckets. This is handled automatically by Sveltia CMS.

## Accessing the Storage

There are two ways to use Backblaze B2 in Sveltia CMS:

### File and Image Fields

When editing content entries, you can use [File](/en/docs/fields/file) and [Image](/en/docs/fields/image) fields to upload and select media on Backblaze B2 directly within the entry editor. Enter your Application Key (secret) in the CMS UI when prompted, and you’ll be able to upload new media directly to B2 or select existing media from your bucket.

When uploading media, files will be stored in your B2 bucket with zero egress fees for downloads. You can also select existing media from your B2 storage.

### Asset Library

Backblaze B2 also appears under **External Locations** in the [Asset Library](/en/docs/ui/asset-library), where you 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](/en/docs/ui/asset-library#folders-on-external-locations): subfolders are listed ahead of the files, uploads go to the folder you’re in, and folders can be created, renamed and deleted.
