Cloudinary Integration
Cachely has first-class support for Cloudinary. It handles both image and video asset URLs and provides a response transformer that rewrites Cloudinary CDN URLs in your API responses.
Supported URL patterns
Cloudinary serves all assets from a single domain using your cloud name and different resource type prefixes:
Images:
https://res.cloudinary.com/{cloudName}/image/upload/v1234567890/folder/photo.jpg
Videos:
https://res.cloudinary.com/{cloudName}/video/upload/v1234567890/folder/clip.mp4
With transformations:
https://res.cloudinary.com/{cloudName}/image/upload/w_800,h_600,c_fill/v1234567890/folder/photo.jpg
Private CDN distribution (Advanced plan and higher):
https://{cloudName}-res.cloudinary.com/image/upload/v1234567890/folder/photo.jpg
All of the above are rewritten to:
https://your-tenant.cachely.io/image/upload/v1234567890/folder/photo.jpg
Tenant setup
When creating your tenant, select Cloudinary as the CMS type and provide your cloud name:
| Field | Value |
|---|---|
| CMS | Cloudinary |
| Cloud name | my-cloud |
| Website domain | your-site.com |
Two origins are automatically configured:
- Image origin:
https://res.cloudinary.com/{cloudName} - Video origin:
https://res.cloudinary.com/{cloudName}
Both use the same base URL since Cloudinary serves all asset types from a single domain. Cachely routes requests to the correct resource type based on the URL path.
Video support
Cloudinary serves images and videos from the same domain (res.cloudinary.com) using different path prefixes (/image/upload/ vs /video/upload/). Cachely handles both automatically.
Range requests for video seeking and streaming are fully supported.
API proxy (read-only)
Cachely can proxy read-only Cloudinary Admin API requests through its /~api/ endpoint. This means your API key and secret are stored encrypted on the server and never exposed to the browser.
Configuration
In the tenant settings under API Proxy:
| Field | Value |
|---|---|
| API Origin URL | https://api.cloudinary.com/v1_1/<cloud_name> |
| Auth mode | Basic auth |
| Token | btoa("api_key:api_secret") — your credentials base64-encoded |
The Cloudinary Admin API uses HTTP Basic authentication with your API key and secret concatenated as api_key:api_secret. Encode this string with btoa() before saving it as the token.
Proxied requests
Any GET request to https://your-tenant.cachely.io/~api/<path> is forwarded to https://api.cloudinary.com/v1_1/<cloud_name>/<path> with the Authorization: Basic <token> header injected automatically.
Examples:
# List image resources
GET https://your-tenant.cachely.io/~api/resources/image
# Search assets
GET https://your-tenant.cachely.io/~api/resources/image/upload?prefix=products
Responses are cached (default 60s) and Cloudinary delivery URLs in the JSON body are rewritten to proxy URLs automatically.
Note: Only
GETrequests are proxied. Upload, rename, destroy, and other write operations are not supported through the Cachely API proxy.
Cachely SDK
Install the Cachely SDK to route Cloudinary asset and API requests through your edge domain:
npm install @cachely-io/sdk
Basic usage
Cloudinary is supported via the SDK's generic provider:
import { createGenericProvider, createCachelyFetch } from '@cachely-io/sdk'
const cloudinary = createGenericProvider({
id: 'cloudinary',
apiHosts: ['api.cloudinary.com'],
assetHosts: ['res.cloudinary.com'],
})
const cachelyFetch = createCachelyFetch({
tenant: 'your-tenant',
provider: cloudinary,
})
// Use as a drop-in fetch — both API and asset URLs are rewritten automatically
const res = await cachelyFetch(
`https://api.cloudinary.com/v1_1/${cloudName}/resources/image`,
)
Rewriting URLs without intercepting fetch
If you can't replace the fetch implementation, use createCachelyUrlRewriter:
import { createCachelyUrlRewriter, createGenericProvider } from '@cachely-io/sdk'
const cloudinary = createGenericProvider({
id: 'cloudinary',
apiHosts: ['api.cloudinary.com'],
assetHosts: ['res.cloudinary.com'],
})
const rewrite = createCachelyUrlRewriter({ tenant: 'your-tenant', provider: cloudinary })
const { url } = rewrite('https://res.cloudinary.com/my-cloud/image/upload/w_800,h_600,c_fill/sample.jpg')
// → https://your-tenant.cachely.io/my-cloud/image/upload/w_800,h_600,c_fill/sample.jpg
Cloudinary transformation segments in the URL path (/w_800,h_600,c_fill/) are preserved end-to-end.
Cloudinary Transformations
Cloudinary's transformation URL API embeds transformations directly in the URL path (e.g. /w_800,h_600,c_fill/). When assets are proxied through Cachely:
- The edge cache uses the full URL path (including transformation segments) as the cache key
- Different transformation variants are cached separately since they have different paths
- This means Cloudinary transformations generally work as expected through the proxy
However, query parameters appended to Cloudinary URLs are stripped from the cache key for image types.
Advanced options
Custom transformers
Add additional transformers that run after the default Cloudinary ones:
const transformed = transformCloudinaryAssetUrls(data, {
cloudName: "my-cloud",
transformers: [
(jsonStr, { base }) => {
return jsonStr.replaceAll("old-pattern", "new-pattern")
}
]
})
Post-transform hook
Run a function on the parsed result after all URL replacements:
const transformed = transformCloudinaryAssetUrls(data, {
cloudName: "my-cloud",
postTransform: (data) => {
return data
}
})
Error handling
By default, transform errors are logged as warnings and the original data is returned unchanged. You can provide a custom error handler:
const transformed = transformCloudinaryAssetUrls(data, {
cloudName: "my-cloud",
onError: (error) => {
Sentry.captureException(error)
}
})