> ## Documentation Index
> Fetch the complete documentation index at: https://docs.convertly.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# CDN performance & origin deliverability

> Cache behaviour, immutable URLs, purge strategy, and troubleshooting when Convertly cannot fetch your origin.

Convertly's image CDN is designed for **long-lived edge cache** and **deterministic URLs**. This guide covers performance best practices and fixing origin fetch failures.

## First request vs repeats

The first request for a new URL — a new file, width, or transform param — may take about **one second** while Convertly encodes that variant. **Repeat requests for the same URL are typically \~20 ms** from the global edge cache. In production, most traffic is cached; stable `srcset` widths and [presets](#presets-reduce-mistakes) keep first-hit requests rare.

## Cache keys and immutability

Every CDN URL is a contract:

```txt theme={"system"}
https://cdn.convertly.sh/{endpointNamespace}/{fileId}?w=1200&format=auto&gravity=smart
```

The edge cache key includes **path + query params + `Accept` + `Save-Data` + Client Hints** when used. That means:

* Changing `w`, `format`, or any transform param creates a **new** cache entry (by design).
* Reusing the same URL always hits the same bytes — ideal for `Cache-Control: immutable` semantics.
* **Do not overwrite files in place** if you need instant updates; upload a new `fileId` or [purge](/docs/image-cdn/operations#purging-the-edge-cache).

### Prefer new file IDs over in-place overwrites

| Pattern                            | Cache behaviour                                  |
| ---------------------------------- | ------------------------------------------------ |
| Upload v2 as new file, update HTML | Old URL keeps serving v1; new URL is fresh       |
| Overwrite bytes at same file ID    | Edge may serve stale bytes until TTL or purge    |
| Purge `asset:{fileId}` tag         | Invalidates all transform variants for that file |

## Format negotiation

Use `format=auto` (SDK default) so modern browsers get AVIF/WebP and legacy clients get JPEG from **one URL string**. Convertly sets `Vary: Accept` — at most two encoded variants per transform URL.

See [Format & delivery](/docs/image-cdn/transforms/format-delivery).

## Responsive delivery

* **Most sites:** `<ConvertlyImage>` or `srcset` + `sizes` — see [Responsive images](/guides/responsive-images).
* **Fluid layouts:** `ch=Width,Dpr` — see [Client Hints guide](/guides/client-hints).
* **Art direction:** `<picture>` with different `w`/`h`/`gravity` per breakpoint.

Include widths up to \~2× your layout size (or use `dpr=2` with a fixed `w` — see [Device pixel ratio](/docs/image-cdn/transforms/resize-crop-fit#device-pixel-ratio)).

## Presets reduce mistakes

Named [presets](/docs/image-cdn/presets-and-signing) (`hero`, `thumb`, `og-card`) keep param sets consistent across teams and reduce accidental cache fragmentation from typos.

## Purge strategy

| Scenario                                   | Recommendation                                                                 |
| ------------------------------------------ | ------------------------------------------------------------------------------ |
| Single known URL changed                   | [Purge by URL](/docs/image-cdn/operations#purge-by-url)                        |
| File replaced in storage, many widths live | [Purge by tag](/docs/image-cdn/operations#purge-by-cache-tag) `asset:{fileId}` |
| Redeployed entire origin folder            | `origin:{slug}` tag purge                                                      |
| Routine content updates                    | New file ID — skip purge                                                       |

## Origin deliverability

Convertly fetches origin-backed URLs (`/o/{slug}/…`) over **public HTTPS** or **private bucket credentials**. If transforms fail or return errors, check the following.

### Public HTTPS origins

| Check                                               | Why it matters                            |
| --------------------------------------------------- | ----------------------------------------- |
| URL returns **200** in a private browser tab        | CDN must fetch without cookies            |
| **HTTPS** with valid certificate                    | HTTP and self-signed are rejected         |
| Not `localhost` / `127.0.0.1` / RFC1918 IPs         | Public CDN cannot reach your laptop       |
| Correct **Content-Type** (`image/jpeg`, etc.)       | Wrong MIME can break encode pipeline      |
| No hotlink blocking that blocks Convertly's fetcher | Some WAF rules block unknown user agents  |
| Response under size limits                          | Very large originals slow first transform |

**Local dev:** use Next.js `localPassthrough` or point the origin at a **preview deploy** or HTTPS tunnel — see [Image CDN setup](/guides/image-cdn-setup) and [Operations → Local development](/docs/image-cdn/operations#local-development).

### Private bucket origins (S3, R2, GCS, Azure)

| Check                                           | Why it matters                                      |
| ----------------------------------------------- | --------------------------------------------------- |
| Read credentials are valid and not expired      | Convertly decrypts stored creds server-side only    |
| Bucket + path prefix match the object key       | `pathPrefix` is prepended to every fetch            |
| IAM / policy allows **GetObject** on the prefix | Write access is not required                        |
| Region / endpoint matches provider              | R2 and custom S3 endpoints need explicit `endpoint` |

Examples for [R2, GCS, and Azure](/docs/image-cdn/operations#origin-sources).

### Common error patterns

| Symptom                                        | Likely cause                                                                                       |
| ---------------------------------------------- | -------------------------------------------------------------------------------------------------- |
| **403** on CDN URL, JSON body about signed URL | Namespace is `signed`/`private` — add `?s=` via [signing API](/docs/image-cdn/presets-and-signing) |
| **502** / upstream fetch failed                | Origin URL 404, timeout, or credentials rejected                                                   |
| Image never updates after deploy               | Browser + CDN cache; bump file id or purge                                                         |
| Works in prod, not locally                     | Expected — use passthrough or public preview origin                                                |

## Smart crop cost

`gravity=smart` and `gravity=face` run analysis on **cache miss** only. Popular URLs pay once, then serve from edge indefinitely. For zero per-request analysis, persist focal points from [`POST /api/images/analyze`](/docs/image-cdn/smart-cropping#the-analyze-endpoint) and render with `fp=x,y`.

## Security vs performance

* **`public`** namespaces: fastest path — embed URLs directly in HTML.
* **`signed` / `private` endpoints**: every URL needs server-side signing. Plan for sign API latency at render time, not per browser request, and cache signed URLs until `exp` when appropriate.

See [Delivery access modes](/docs/image-cdn/operations#delivery-access-modes).

## Related docs

* [Operations](/docs/image-cdn/operations) — purge, origins, security
* [URL structure](/docs/image-cdn/url-structure) — namespaces, slugs, custom domains
