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:

FieldValue
CMSCloudinary
Cloud namemy-cloud
Website domainyour-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:

FieldValue
API Origin URLhttps://api.cloudinary.com/v1_1/<cloud_name>
Auth modeBasic auth
Tokenbtoa("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 GET requests 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)
  }
})
Need help understanding this?Ask Cachely Copilot about features, setup, or integrations.
Ask Copilot →