> For the complete documentation index, see [llms.txt](https://mercure-technologies.gitbook.io/xprem/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://mercure-technologies.gitbook.io/xprem/cdn/generic-cdn.md).

# Generic CDN

If you already have a CDN or an accelerated domain in front of your storage bucket, you can serve update assets through it without any provider-specific integration.

This works with both cloud storage backends: set up [S3 Storage](/xprem/storage/s3-storage.md) or [Google Cloud Storage](/xprem/storage/google-cloud-storage.md) first, then put your CDN in front of the bucket.

{% hint style="warning" %}
The generic CDN only works with public endpoints: every asset behind the CDN domain must be reachable without a signature or token. If you need private delivery, use [CloudFront](/xprem/cdn/cloudfront.md) instead.
{% endhint %}

### Configure the base URL

Point `CDN_BASE_URL` at your CDN domain:

```dotenv
CDN_BASE_URL=https://cdn.example.com
```

{% hint style="info" %}
`CDN_BASE_URL` replaces the deprecated `S3_CDN_PREFIX` variable, named back when S3 was the only cloud storage backend. `S3_CDN_PREFIX` still works so existing deployments keep running, but the server logs a deprecation warning at boot and the variable will be removed in a future release. When both are set, `CDN_BASE_URL` wins.
{% endhint %}

From then on, the server answers each asset request with a redirect to the CDN:

```
https://cdn.example.com/{appId}/{branch}/{runtimeVersion}/{updateId}/{asset}
```

If your CDN expects the bucket name in the path, simply include it in the base URL:

```dotenv
CDN_BASE_URL=https://cdn.example.com/my-bucket
```

### Cloudflare R2

The most common use of the generic CDN is Cloudflare in front of an R2 bucket. R2 speaks the S3 API, so the storage side is the regular `s3` mode pointed at your R2 endpoint:

```dotenv
STORAGE_MODE=s3
S3_BUCKET_NAME=my-ota-bucket
AWS_BASE_ENDPOINT=https://<account-id>.r2.cloudflarestorage.com
AWS_REGION=auto
AWS_ACCESS_KEY_ID=<r2-access-key-id>
AWS_SECRET_ACCESS_KEY=<r2-secret-access-key>
CDN_BASE_URL=https://cdn.example.com
```

For the CDN side, do not point `CDN_BASE_URL` at the `r2.cloudflarestorage.com` endpoint: that endpoint is private and expects signed S3 requests. Instead, connect a custom domain to the bucket from the Cloudflare dashboard (R2, your bucket, Settings, Custom domains). The custom domain exposes the bucket publicly and serves it through the Cloudflare network, so update assets are cached at the edge. Point `CDN_BASE_URL` at that domain.

This combination works particularly well for OTA delivery:

* The bucket layout is immutable: every update lives under its own `updateId` and assets carry hashed names, so cached objects never go stale and you never need to purge. A "cache everything" rule on the domain is safe if you want to maximize the hit rate.
* R2 has no egress fees, and cache hits never reach the bucket at all, which makes this one of the cheapest ways to distribute updates.

The public-endpoint trade-off described above still applies: anyone holding a URL can download the assets behind the custom domain.

### Good to know

* If you use `BUCKET_KEY_PREFIX`, the server already adds it to the generated URLs, so don't repeat it in `CDN_BASE_URL` or it would be applied twice.
* CloudFront takes precedence over the generic CDN whenever its domain, key pair ID and private key are all configured.
* On GCS, a configured `CDN_BASE_URL` takes precedence over signed URL delivery: if you configure a CDN explicitly, the server assumes you want assets served through it. Keep in mind this requires the bucket to be public, while signed URL delivery keeps it private.
* The CDN only caches what the bucket serves; the server keeps writing updates directly to the bucket as usual.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://mercure-technologies.gitbook.io/xprem/cdn/generic-cdn.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
