Advanced URL delivery options

Cloudinary offers a variety of advanced URL options for delivering your media assets.

Asset versions

By default, Cloudinary assigns a randomly generated unique public ID to each uploaded media asset. Alternatively, you can either define your own custom public ID or one based on the original file name of the uploaded image. If you upload an image with a public ID that already exists, the file will be overwritten. Keep in mind though that the CDN may already contain a previously cached copy of the older image.

To force the CDN to display the latest uploaded image, you can add a version component to Cloudinary's URLs. The version value is returned by Cloudinary as part of the response of the image upload, and represents the timestamp of the upload. Adding the version component to URLs can be done by setting the version parameter (v in URLs), for example:

Ruby:
Copy to clipboard
cl_image_tag("sample.jpg")
PHP:
Copy to clipboard
cl_image_tag("sample.jpg")
Python:
Copy to clipboard
CloudinaryImage("sample.jpg").image()
Node.js:
Copy to clipboard
cloudinary.image("sample.jpg")
Java:
Copy to clipboard
cloudinary.url().imageTag("sample.jpg");
JS:
Copy to clipboard
cloudinary.imageTag('sample.jpg').toHtml();
jQuery:
Copy to clipboard
$.cloudinary.image("sample.jpg")
React:
Copy to clipboard
<Image publicId="sample.jpg" >

</Image>
Vue.js:
Copy to clipboard
<cld-image publicId="sample.jpg" >

</cld-image>
Angular:
Copy to clipboard
<cl-image public-id="sample.jpg" >

</cl-image>
.Net:
Copy to clipboard
cloudinary.Api.UrlImgUp.BuildImageTag("sample.jpg")
Android:
Copy to clipboard
MediaManager.get().url().generate("sample.jpg");
iOS:
Copy to clipboard
imageView.cldSetImage(cloudinary.createUrl().generate("sample.jpg")!, cloudinary: cloudinary)
sample image with version

As an alternative to using versions, you can set the invalidate parameter to true while uploading a new image in order to invalidate the previous image throughout the CDN. Note that it usually takes a few minutes (although it might take up to an hour) for the invalidation to fully propagate through the CDN, while the version component takes effect immediately.

Notes

  • There are also a number of other important considerations when using the invalidate functionality. For example, if there is no version number in a URL that includes a folder structure, then by default, those URLs are not invalidated. For details on invalidating media assets, see Invalidating cached media assets on the CDN.
  • You cannot use 'v' followed by numeric characters as the name of a folder.

Error handling

If you have a problem when accessing a Cloudinary transformation URL (e.g., a blank result in your browser), it might be a simple matter of using the wrong transformation syntax. To understand more, check the X-Cld-Error HTTP response header which is where Cloudinary reports any errors it encounters (e.g., invalid syntax, invalid parameters, limit issues, etc.).

For example, trying to access the following URL would result in a X-Cld-Error: Invalid width - abc error, as the width parameter is used with an integer value and not a string:

https://res.cloudinary.com/demo/image/upload/w_abc/sample.jpg

To view the X-Cld-Error header on a Chrome browser for example, select Developers Tools from the View menu. Then select the Network tab, refresh your page, click on the image name with the 400 status code and look for X-Cld-Error under Response Headers.

Error report

You can also use the Management Console to see errors that have been generated from requests to your account via delivery URLs or API calls. To access the Error Report page click Reports > Error report.

Error report

You can export an error report for a specific day by selecting the date and clicking the Export to CSV link. An email is sent to you with a link to download the error report in CSV format.

Client-side resources

You can use a list delivery type to generate a list of resources with a specified tag. Additional information is returned for each resource, including information on its format, type, dimensions, context and metadata.

URL syntax:

Copy to clipboard
https://res.cloudinary.com/<your_cloud_name>/<resource_type>/list/<tag>.json

The response is a JSON snippet listing all the resources of the specified resource type and the specified tag. For example, the request below generates a JSON list of all image resources in the demo project with the tag logo.

Copy to clipboard
https://res.cloudinary.com/demo/image/list/logo.json

You can also use our SDKS to generate the URL:

Ruby:
Copy to clipboard
cloudinary_url("logo.json", :type=>"list")
PHP:
Copy to clipboard
cloudinary_url("logo.json", array("type"=>"list"))
Python:
Copy to clipboard
cloudinary_url("logo.json", type="list")
Node.js:
Copy to clipboard
cloudinary.url("logo.json", {type: "list"})
Java:
Copy to clipboard
cloudinary.url().type("list").generate("logo.json");
JS:
Copy to clipboard
cloudinary.url('logo.json', {type: "list"});
jQuery:
Copy to clipboard
$.cloudinary.url("logo.json", {type: "list"})
.Net:
Copy to clipboard
cloudinary.Api.UrlImgUp.Type("list").BuildImageTag("logo.json")
Android:
Copy to clipboard
MediaManager.get().url().type("list").generate("logo.json");
iOS:
Copy to clipboard
imageView.cldSetImage(cloudinary.createUrl().setType( "list").generate("logo.json")!, cloudinary: cloudinary)

JSON response from the URL:

Copy to clipboard
 {"resources":
   [{"public_id":"cloudinary_fashion_logo","version":1588962712,"format":"png",
     "width":240,"height":307,"type":"upload","created_at":"2020-05-08T18:31:52Z",
     "metadata":[{"external_id":"color_id","label":"Colors","type":"set","value":
     [{"external_id":"color1","value":"red"},{"external_id":"color2","value":"green"}]}]},
   {"public_id":"amazon_logo","version":1315740184,"format":"png",
     "width":162,"height":38,"type":"upload","created_at":"2011-09-11T11:23:04Z"},
   {"public_id":"microsoft_logo","version":1315740090,"format":"png",
     "width":216,"height":70,"type":"upload","created_at":"2011-09-11T11:21:30Z"},
   {"public_id":"apple_logo","version":1315740074,"format":"jpg",
     "width":206,"height":250,"type":"upload","created_at":"2011-09-11T11:21:14Z",
     "context":{"custom":{"Main":"true","Order":"2"}}},
   {"public_id":"google_logo","version":1315740052,"format":"png",
     "width":275,"height":95,"type":"upload","created_at":"2011-09-11T11:20:52Z",
     "context":{"custom":{"alt":"google logo","caption":"Google Logo"}}}], 
     "updated_at":"2020-05-08T21:11:37Z"}

Notes

  • By default, the list delivery type is restricted. To enable it, open the Security settings in your Management console, and clear the checkmark from the Resource list item under Restricted image types. You may want to clear this option only temporarily, as needed. Alternatively, you can bypass this (and any) delivery type restriction using a signed URL.
  • This option supports listing up to 1000 resources. To view more than 1000, use the resources_by_tag property of the Admin API, and include the next_cursor parameter to view long lists.
  • The JSON response is cached with a 60 second expiration policy.

Private CDNs and CNAMEs

The general Cloudinary shared CDN distribution is https://res.cloudinary.com and so image delivery URLs follow the format as described above:

https://res.cloudinary.com/<cloud_name>/image/upload/<public ID>.<extension>

If you use a private CDN distribution configuration, the image delivery URLs follow the format:

https://<cloud_name>-res.cloudinary.com/image/upload/<public ID>.<extension>

For example:

https://demo-res.cloudinary.com/image/upload/sample.jpg

For customers with a Custom Domain Name (CNAME), the image delivery URL becomes:

https://<custom domain name>/image/upload/<public ID>.<extension>

For example:

https://www.example.com/image/upload/sample.jpg

Note
CNAMEs and private CDN distributions are available for Cloudinary's Advanced plan and above, and require a small setup on Cloudinary's side. An HTTPS-enabled CNAME also entails an additional cost. Contact us for more details.

Multi-CDN Solutions

Different CDN solutions may provide better support for a particular CDN-based functionality that is important for your website or application, such as quick invalidations, video streaming capabilities, and more.

Alternatively, you may find that one CDN service best serves certain geographical markets, while another is significantly better for other locations. The same may be true for the same geography, but at different times of the day, or for a variety of other variables. Therefore, you could potentially optimize your page load time for each user by using different CDN services for different user requests.

If your account is on a Custom plan, you can optionally take advantage of one of the following solutions to ensure that your resources are delivered via the CDN that bests fits the needs of your application and your users:

  • Dynamic multi-CDN switching: Uses real-time data to automatically select the best performing or most appropriate of the supported CDN services for every user request. This automated CDN switching service routes users based on geography, HTTP round-trip-time (RTT), and a variety of other variables, and directs each user to a specific web server based on intersections between server load and geography, while avoiding CDNs that are experiencing temporary outages or lowered availability. It gathers this data on an ongoing basis so that it is ready to make the best real-time routing decision each time a request comes in.

    • Take a look at the Multi-CDN Demo page to see how long the same resource takes to deliver to your browser or to a variety of geographies via different CDN services, and how quickly you can deliver that same resource via the multi-CDN switching feature.
    • This service includes real-time monitoring that can provide insights about how improvements in your page load time influence user behavior KPIs such as visit length and conversions.
  • Smart CDN selection: Cloudinary experts help you determine which of the supported CDN services is the best fit for your required features and target audience. In some cases, it may even be determined that certain types of resources should be delivered through one CDN service, and the remainder through another. Additionally, if it’s determined in the future that another CDN service could better serve your content, Cloudinary transparently implements the new configuration and mapping behind the scenes.

In both of the above options, Cloudinary handles the required CDN configurations and the mappings between CDN domains and the public Cloudinary domain, so that the resource URLs in your site or app never need to change.

These multi-CDN features are available only to Cloudinary customers with Custom Plan accounts, and the dynamic multi-CDN switching feature affects pricing. These features are currently supported for Akamai, Fastly and CloudFront CDNs. Contact us for additional details.

Multiple sub-domains

Note
The multiple sub-domains feature is no longer necessary with Cloudinary supporting the newer HTTP 2.0 protocol, since HTTP 2.0 supports concurrency by default.

Browsers currently limit the number of download requests they perform concurrently from each unique domain, which means that downloading many images from a single web page might be somewhat slow. To overcome this limitation, Cloudinary supports downloading images via multiple sub-domains, which means you can spread your image downloads between the different sub-domains, resulting in a much improved web browsing speed.

The improvement applies only to image requests over HTTP and it is not recommended to use multiple sub-domains together with HTTPS: opening an HTTPS connection requires more time than opening an HTTP connection (several round trips are needed to perform the encryption handshake).

Cloudinary supports the default res sub-domain, as well as the res-1 through res-5 sub-domains, as a prefix to the host name. Mapping between a specific image (by public ID) and the six different sub-domains (res plus res-1 through res-5) should be consistent in order to ensure that the browsers can correctly cache the images. For example, referencing a certain image once via 'res-1' and then again via 'res-2' will bypass browser caching and download the image twice.

For example, the sample image delivered via the res-4 sub-domain:

https://res-4.cloudinary.com/demo/image/upload/sample.jpg

When using one of Cloudinary's framework SDKs, you can also automatically enable delivering images from the res-1 through res-5 sub-domains by setting the cdn_subdomain parameter to true, either globally (e.g., in the CLOUDINARY_URL environment variable) or locally in each call. Note that the Cloudinary SDKs automatically map individual images consistently to the same sub-domain using a CRC32 based code of the image's public ID.

You can also use multiple sub-domains when using a custom domain (CNAME) by prepending the a1 through a5 sub-domains, as a prefix to the host name, for example:

https://a1.<mydomain.com>/image/upload/sample.jpg

Note
CNAMEs are available only for Cloudinary's Advanced plan and above, and requires a small setup on Cloudinary's side. Furthermore, the sub-domains need to be defined in the DNS settings of the domain name. Contact us for more details.

For more information on using sub-domains, see the Reduce site load time with multiple CDN sub-domains article.

SEO-friendly media asset URLs

Image and video URLs are specified in the source code of HTML pages and are leveraged by search engines to understand the media asset's content. Concise and descriptive image and video file names are better for search engines to extract information about a media file, therefore supporting your site's SEO ranking.

SEO starts with the ability to define custom public IDs while uploading media assets which can be as descriptive as necessary. Cloudinary can help make your image and video URLs more SEO friendly in a few other ways:

For more information on creating SEO friendly URLs, see the article on How to dynamically create SEO-friendly URLs for your site's images.

Root path URLs

The Root Path URL feature allows you to create shorter image URLs for delivering uploaded images. The default Cloudinary resource delivery URL includes the resource type and type parameters, for example, /image/upload. With Cloudinary’s Root Path URL feature, the resource type and type parameters are set to the default values 'image' and 'upload' respectively, which means that any URL without the resource type and type parameters will automatically default to using those values.

For example, the default delivery URL for the sample image in JPEG format is normally:

https://res.cloudinary.com/demo/image/upload/sample.jpg

The delivery URL using the Root Path URL feature for image uploads is:

https://res.cloudinary.com/demo/sample.jpg

Both the URLs above deliver the same uploaded image.

Dynamic SEO suffixes

The dynamic SEO suffixes feature allows you to dynamically add a descriptive suffix to the Public ID in the delivery URL. This can be useful:

  • If the asset was not given a suitable Public ID during the upload process.
  • To support different languages for describing a single asset.
  • To reflect specific content on certain pages.

To add a dynamic SEO suffix, the type and resource type need to be merged into a single component. The default /image/upload or /video/upload becomes just images or videos accordingly. Afterwards, any custom suffix can then be dynamically appended to the Public ID by adding a slash (/) and the SEO name.

For example the default delivery URL for the t8sn7wg4jd74j image in JPEG format is:

https://demo-res.cloudinary.com/image/upload/t8sn7wg4jd74j.jpg

The delivery URL with the suffix basketball-game added to the Public ID is:

https://demo-res.cloudinary.com/images/t8sn7wg4jd74j/basketball-game.jpg

In the URL below, the same image is given a suffix in Spanish:

https://demo-res.cloudinary.com/images/t8sn7wg4jd74j/baloncesto-juego.jpg

All the URLs above deliver the same uploaded image.

Note
You can also use dynamic SEO suffixes for raw files and private or authenticated images:

  • For raw files, replace /raw/upload with /files
  • For private image uploads, replace /image/private with /private_images
  • For authenticated uploads, replace image/authenticated with /authenticated_images.

Currently, private and authenticated videos do not support SEO suffixes.

CNAME

You can also make your URLs more SEO friendly by using a custom domain (CNAME) for your URLs instead of the shared res.cloudinary.com. The dynamic SEO suffix and CNAME features can also be used together, for example:

https://<mydomain.com>/images/t8sn7wg4jd74j/basktetball-game.jpg

Note
CNAMEs are available for Cloudinary's Advanced plan and above, and requires a small setup on Cloudinary's side. Contact us for more details.

Using a default image placeholder

Default images can be used in the case that a requested image does not exist. For example, a site that automatically stores user profile pictures with the same name as the user themselves, allowing you to reference the pictures by user name (unless the user has not uploaded a profile picture yet). Specify a default image to use with the default_image parameter (d in URLs) and the public ID + format of a previously uploaded image, for example, d_placeholder.png to use the image with the public ID of placeholder as the default image. Any requested transformations are also applied on the default image as well.

For example, to use the PNG image called avatar as a default image in the case that the image called non_existing_id does not exist:

Ruby:
Copy to clipboard
cl_image_tag("non_existing_id.png", :default_image=>"avatar.png")
PHP:
Copy to clipboard
cl_image_tag("non_existing_id.png", array("default_image"=>"avatar.png"))
Python:
Copy to clipboard
CloudinaryImage("non_existing_id.png").image(default_image="avatar.png")
Node.js:
Copy to clipboard
cloudinary.image("non_existing_id.png", {default_image: "avatar.png"})
Java:
Copy to clipboard
cloudinary.url().transformation(new Transformation().defaultImage("avatar.png")).imageTag("non_existing_id.png");
JS:
Copy to clipboard
cloudinary.imageTag('non_existing_id.png', {defaultImage: "avatar.png"}).toHtml();
jQuery:
Copy to clipboard
$.cloudinary.image("non_existing_id.png", {default_image: "avatar.png"})
React:
Copy to clipboard
<Image publicId="non_existing_id.png" >
  <Transformation defaultImage="avatar.png" />
</Image>
Vue.js:
Copy to clipboard
<cld-image publicId="non_existing_id.png" >
  <cld-transformation defaultImage="avatar.png" />
</cld-image>
Angular:
Copy to clipboard
<cl-image public-id="non_existing_id.png" >
  <cl-transformation default-image="avatar.png">
  </cl-transformation>
</cl-image>
.Net:
Copy to clipboard
cloudinary.Api.UrlImgUp.Transform(new Transformation().DefaultImage("avatar.png")).BuildImageTag("non_existing_id.png")
Android:
Copy to clipboard
MediaManager.get().url().transformation(new Transformation().defaultImage("avatar.png")).generate("non_existing_id.png");
iOS:
Copy to clipboard
imageView.cldSetImage(cloudinary.createUrl().setTransformation(CLDTransformation().setDefaultImage("avatar.png")).generate("non_existing_id.png")!, cloudinary: cloudinary)
avatar used as default image when requested image does not exist

The same example as above, but with transformation parameters applied to scale down to 200 pixels wide and rotate by 45 degrees:

Ruby:
Copy to clipboard
cl_image_tag("non_existing_id.png", :transformation=>[
  {:width=>200, :angle=>45, :crop=>"scale"},
  {:default_image=>"avatar.png"}
  ])
PHP:
Copy to clipboard
cl_image_tag("non_existing_id.png", array("transformation"=>array(
  array("width"=>200, "angle"=>45, "crop"=>"scale"),
  array("default_image"=>"avatar.png")
  )))
Python:
Copy to clipboard
CloudinaryImage("non_existing_id.png").image(transformation=[
  {'width': 200, 'angle': 45, 'crop': "scale"},
  {'default_image': "avatar.png"}
  ])
Node.js:
Copy to clipboard
cloudinary.image("non_existing_id.png", {transformation: [
  {width: 200, angle: 45, crop: "scale"},
  {default_image: "avatar.png"}
  ]})
Java:
Copy to clipboard
cloudinary.url().transformation(new Transformation()
  .width(200).angle(45).crop("scale").chain()
  .defaultImage("avatar.png")).imageTag("non_existing_id.png");
JS:
Copy to clipboard
cloudinary.imageTag('non_existing_id.png', {transformation: [
  {width: 200, angle: 45, crop: "scale"},
  {defaultImage: "avatar.png"}
  ]}).toHtml();
jQuery:
Copy to clipboard
$.cloudinary.image("non_existing_id.png", {transformation: [
  {width: 200, angle: 45, crop: "scale"},
  {default_image: "avatar.png"}
  ]})
React:
Copy to clipboard
<Image publicId="non_existing_id.png" >
  <Transformation width="200" angle="45" crop="scale" />
  <Transformation defaultImage="avatar.png" />
</Image>
Vue.js:
Copy to clipboard
<cld-image publicId="non_existing_id.png" >
  <cld-transformation width="200" angle="45" crop="scale" />
  <cld-transformation defaultImage="avatar.png" />
</cld-image>
Angular:
Copy to clipboard
<cl-image public-id="non_existing_id.png" >
  <cl-transformation width="200" angle="45" crop="scale">
  </cl-transformation>
  <cl-transformation default-image="avatar.png">
  </cl-transformation>
</cl-image>
.Net:
Copy to clipboard
cloudinary.Api.UrlImgUp.Transform(new Transformation()
  .Width(200).Angle(45).Crop("scale").Chain()
  .DefaultImage("avatar.png")).BuildImageTag("non_existing_id.png")
Android:
Copy to clipboard
MediaManager.get().url().transformation(new Transformation()
  .width(200).angle(45).crop("scale").chain()
  .defaultImage("avatar.png")).generate("non_existing_id.png");
iOS:
Copy to clipboard
imageView.cldSetImage(cloudinary.createUrl().setTransformation(CLDTransformation()
  .setWidth(200).setAngle(45).setCrop("scale").chain()
  .setDefaultImage("avatar.png")).generate("non_existing_id.png")!, cloudinary: cloudinary)
avatar used as default image when requested image does not exist

Note
If the requested image does not exist and the default placeholder image is delivered instead, the x_cld_error header will also be included in the response.

Generating delivery URL signatures

Cloudinary delivery URLs require a signature component under the following circumstances:

  • Authenticated media assets - all assets uploaded with their resource_type set to authenticated
  • Dynamic transformations with strict transformations enabled - this only applies to generating and then delivering new derived assets dynamically (on-the-fly).
  • Dynamic transformations with certain add-ons - this only applies if the add-on has never been used with the asset before. If you use a Cloudinary add-on that supports on-the-fly activation of the add-on capability in a transformation URL, check the relevant add-on documentation for the signature requirements.

Important

  • The signature component is automatically generated and added to the URL when you use one of Cloudinary's SDK helper methods you include the sign_url boolean parameter set to true.
  • api_secret, which is a required element of in signature generation, should never be revealed to anyone who is not authorized, and therefore your signature should never be generated on the client side or inside your native application.

To manually create a signed delivery URL, you also need to create a signature component of the format /s--SIGNATURE--/ that is based on the Public ID and any transformations or version number you use in the rest of the delivery URL. The SIGNATURE is the first 8 characters of a URL-safe base64 message digest (hash value) created with the SHA-1 or SHA-256 (Secure Hash Algorithm) cryptographic function.

Note
By default, SHA-1 digest is used to create and verify all Cloudinary signatures. To instead use the SHA-256 digest for all your account signatures, submit a request.

To generate the URL signature:

  1. Create a single string including all of the directives for the asset to deliver: any transformation parameters, the version number, the public_id, and file extension that will be used in the delivery URL, separating each component with slashes (/) (this string is exactly equivalent to the components of the delivery URL that will come after the signature).
  2. Append your API secret to the end of the string.
  3. Create a URL-safe base64 message digest (hash value) of the string using SHA-1 (or SHA-256, if relevant for your account).

For example, if your API secret is abcd, and you need to generate a signature for the sample image scaled to 300x250, with a grayscale effect (w_300,h_250,e_grayscale), and delivered as a PNG:

  • Parameters to sign:
    • w_300,h_250,e_grayscale
    • sample.png
  • Parameters in a single string joined with a slash:
    • w_300,h_250,e_grayscale/sample.png
  • String including the API secret that is used to create the signature:
    • w_300,h_250,e_grayscale/sample.pngabcd
  • SHA-1 base64 result:
    • INQUGuluWsGzxkcBaITPo7KMKic
  • First 8 characters to use as URL signature:
    • INQUGulu
  • Full signature component including prefix and suffix:
    • s--INQUGulu--

The final delivery URL including the signature:

https://res.cloudinary.com/demo/image/upload/s--INQUGulu--/w_300,h_250,e_grayscale/sample.png

An example of the above in Ruby on Rails:

Copy to clipboard
transformation = "w_300,h_250,e_grayscale"
public_id = "sample.png"
secret = "abcd"

to_sign = ([transformation, public_id]).join("/")
signature = 's--' + Base64.urlsafe_encode64(Digest::SHA1.digest(to_sign + secret))[0,8] + '--'
url = 'https://res.cloudinary.com/demo/image/upload/' + ([signature, to_sign]).join("/")

See also: Have a look a the Cloudinary Signatures quick reference for a summary of the payload string to sign for delivery URL signatures as well as information on other use cases that may require signature generation.