Media access control

When storing images, videos or other files in Cloudinary, both the originals and their transformed versions are publicly available through a CDN, by default. You can use randomly generated Public IDs for your assets, which makes it harder for end users to guess your media URLs, but you may want more formal ways to control who can access your media files and when.

Strict transformations

Cloudinary's manipulation URLs are dynamic, which means that if the requested transformed asset does not already exist, then it is created on-the-fly. This is a powerful feature, but you may not want your end users to play with these options on your assets. To control this, you can enable Strict Transformations on your account to prevent transformations from being dynamically applied to media assets. Except for any transformations that you specifically allow to be used dynamically, your users will be restricted to accessing only pre-generated transformed assets (generated eagerly during upload or with an authenticated request to our API).

Enable the Strict Transformations setting in your account console's Security settings. To mark a specific transformation as allowed, either use the Admin API or open the Transformations tab of the console. Next to each transformation is an icon that can be toggled to either allow or restrict the transformation.

enable transformation

Trying to dynamically generate and deliver an allowed transformation will work:

Ruby:
Copy to clipboard
cl_image_tag("sheep.jpg", :height=>200, :width=>300, :radius=>20, :crop=>"fill")
PHP:
Copy to clipboard
cl_image_tag("sheep.jpg", array("height"=>200, "width"=>300, "radius"=>20, "crop"=>"fill"))
Python:
Copy to clipboard
CloudinaryImage("sheep.jpg").image(height=200, width=300, radius=20, crop="fill")
Node.js:
Copy to clipboard
cloudinary.image("sheep.jpg", {height: 200, width: 300, radius: 20, crop: "fill"})
Java:
Copy to clipboard
cloudinary.url().transformation(new Transformation().height(200).width(300).radius(20).crop("fill")).imageTag("sheep.jpg");
JS:
Copy to clipboard
cloudinary.imageTag('sheep.jpg', {height: 200, width: 300, radius: 20, crop: "fill"}).toHtml();
jQuery:
Copy to clipboard
$.cloudinary.image("sheep.jpg", {height: 200, width: 300, radius: 20, crop: "fill"})
React:
Copy to clipboard
<Image publicId="sheep.jpg" >
  <Transformation height="200" width="300" radius="20" crop="fill" />
</Image>
Vue.js:
Copy to clipboard
<cld-image publicId="sheep.jpg" >
  <cld-transformation height="200" width="300" radius="20" crop="fill" />
</cld-image>
Angular:
Copy to clipboard
<cl-image public-id="sheep.jpg" >
  <cl-transformation height="200" width="300" radius="20" crop="fill">
  </cl-transformation>
</cl-image>
.Net:
Copy to clipboard
cloudinary.Api.UrlImgUp.Transform(new Transformation().Height(200).Width(300).Radius(20).Crop("fill")).BuildImageTag("sheep.jpg")
Android:
Copy to clipboard
MediaManager.get().url().transformation(new Transformation().height(200).width(300).radius(20).crop("fill")).generate("sheep.jpg");
iOS:
Copy to clipboard
imageView.cldSetImage(cloudinary.createUrl().setTransformation(CLDTransformation().setHeight(200).setWidth(300).setRadius(20).setCrop("fill")).generate("sheep.jpg")!, cloudinary: cloudinary)
allowed transformation

Trying to access any other transformation, either disallowed or non-existing, will not succeed.

Important
There are certain considerations to be aware of if enabling Strict Transformations after using your Cloudinary account for awhile. For example, the option to apply a transformation to an image on-the-fly is blocked only from the time that the strict transformations feature is enabled or from the time that a previously Allowed transformation is changed to Disallowed. Any derived image that was generated while a particular transformation was still allowed, remains accessible. Certain transformation combinations also need to be adapted to work with Strict Transformations enabled, so we recommend that you contact us to help identify any potential issues with the transition.

Allowed strict referral domains

You can use the Allowed strict referral domains setting to set the referrer domains that are allowed to generate unsigned dynamic transformations, even when strict transformations are enabled. This setting can be found in your account console's Security settings.

Signed delivery URLs

A signed Cloudinary delivery URL is a dynamic URL that has its signature validated before making it available for view (see the article about on the fly image manipulations secured with signed urls for more details). Signed delivery URLs are generally used to:

  • Deliver specific derived assets when strict transformations are enabled
  • Apply add-on based manipulation directives that require signing the URL
  • Fetch assets from specific URLs when "Fetched URLs" are restricted
  • Allow access to private/authenticated assets

A signed delivery URL contains a signature component of the format /s--SIGNATURE--/. The signature is automatically generated by Cloudinary's server-side SDKs by adding the sign_url boolean parameter to the helper method and setting it to true (you can manually generate a signature by taking the first 8 characters of a base64 encoding of a SHA-1 (or SHA-256) digest of a 'public_id/transformation' string concatenated with your API secret. See Generating delivery URL signatures for more information).

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.

For example, to create an image tag for the authenticated image secret_couple resized to a height and width of 300 pixels using the fill resize mode, while signing the transformation URL using the first 8 characters of the SHA-1 signature.

Ruby:
Copy to clipboard
cl_image_tag("secret_couple.jpg", :height=>300, :width=>300, :crop=>"fill", :sign_url=>true, :type=>"authenticated")
PHP:
Copy to clipboard
cl_image_tag("secret_couple.jpg", array("height"=>300, "width"=>300, "crop"=>"fill", "sign_url"=>true, "type"=>"authenticated"))
Python:
Copy to clipboard
CloudinaryImage("secret_couple.jpg").image(height=300, width=300, crop="fill", sign_url=True, type="authenticated")
Node.js:
Copy to clipboard
cloudinary.image("secret_couple.jpg", {height: 300, width: 300, crop: "fill", sign_url: true, type: "authenticated"})
Java:
Copy to clipboard
cloudinary.url().transformation(new Transformation().height(300).width(300).crop("fill")).signed(true).type("authenticated").imageTag("secret_couple.jpg");
.Net:
Copy to clipboard
cloudinary.Api.UrlImgUp.Transform(new Transformation().Height(300).Width(300).Crop("fill")).Signed(true).Action("authenticated").BuildImageTag("secret_couple.jpg")

And the same for a video tag, using the same transformations:

Ruby:
Copy to clipboard
cl_video_tag("dog", :height=>300, :width=>300, :crop=>"fill", :sign_url=>true, :type=>"authenticated")
PHP:
Copy to clipboard
cl_video_tag("dog", array("height"=>300, "width"=>300, "crop"=>"fill", "sign_url"=>true, "type"=>"authenticated"))
Python:
Copy to clipboard
CloudinaryVideo("dog").video(height=300, width=300, crop="fill", sign_url=True, type="authenticated")
Node.js:
Copy to clipboard
cloudinary.video("dog", {height: 300, width: 300, crop: "fill", sign_url: true, type: "authenticated"})
Java:
Copy to clipboard
cloudinary.url().transformation(new Transformation().height(300).width(300).crop("fill")).signed(true).type("authenticated").videoTag("dog");
.Net:
Copy to clipboard
cloudinary.Api.UrlVideoUp.Transform(new Transformation().Height(300).Width(300).Crop("fill")).Signed(true).Action("authenticated").BuildVideoTag("dog")

Once a derived image or video has been generated (whether dynamically on first access or eagerly during upload), the signature checking is skipped and the signature itself can be omitted (except for Authenticated assets, which always require either a signature or another authentication token).

Private media assets

Images and videos can be uploaded as private to restrict access to the original asset and only allow access to derived (transformed) versions of the asset. The original asset can be accessed only with a signed URL, but by default, all derived versions of the asset are accessible. To deliver a transformation of an asset uploaded as private, set the type parameter in the delivery URL to private (instead of the default upload).

An asset that was uploaded as ‘private’ cannot be accessed publicly without a signed URL. For example, the following delivery URL for the sample image returns an error:

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

Delivering any derived versions of the image is still possible. For example, the sample private image with a width of 300 pixels:

https://res.cloudinary.com/demo/image/private/w_300/sample.jpg

By default, all derived versions of the image are accessible, but the generation of derived images can also be restricted by activating the Strict Transformations mode. This mode prevents dynamically generating derived versions of the image, except for those that have been specifically enabled (e.g., with watermarks) that can then be generated on the fly. With Strict Transformations enabled you need to either eagerly generate all derived images or mark specific dynamic transformations as allowed.

Providing time-limited access to private media assets

You can make a private original asset temporarily accessible (for example, to enable a customer to access a stock photo on your site after purchasing it) by generating a time-limited and signed URL with the private_download_url method of the Cloudinary API. For example, to generate a link to the my_picID original image in JPEG format:

Ruby:
Copy to clipboard
Cloudinary::Utils.private_download_url('my_picID', 'jpg')
PHP:
Copy to clipboard
\Cloudinary\Utils::private_download_url('my_picID', 'jpg');
Python:
Copy to clipboard
cloudinary.utils.private_download_url('my_picID', 'jpg')
Node.js:
Copy to clipboard
cloudinary.v2.utils.private_download_url('my_picID', 'jpg', 
    function(error, result) { console.log(result); });
Java:
Copy to clipboard
cloudinary.utils().privateDownload("my_picID", "jpg", 
    ObjectUtils.emptyMap());

This generates a link similar to the following:

https://api.cloudinary.com/v1_1/private-demo/image/download?api_key=824698761754661&format=jpg &public_id=my_picID&signature=d994c2b972c30d84d33fde684aa377fc17878be6&timestamp=1346076992

The generated URL delivers the image via a secure authenticated API request to Cloudinary each time. The image is not cached on the CDN.

Required parameters

  • public_id - The identifier of the uploaded asset.
  • format - The asset file format.

Optional parameters:

  • resource_type - The resource type (image, video or raw) of the file to deliver. Default: image.
  • type - The delivery type of the file to deliver. Possible values: upload, private, authenticated, Default: private.
  • expires_at - The date (UNIX time in seconds) for the URL expiration. Default: 1 hour from the time the URL is generated.
  • attachment- If true, the URL downloads the image as an attachment. Otherwise, the image is displayed. Default: false.

Authenticated access to media assets

Images and videos can be stored with an authenticated delivery type, or they can be temporarily set as an authenticated asset using the access_mode or access_control parameter of the Upload or Update methods.

The authenticated delivery type is an element of the URL. Therefore, an asset uploaded with this type is always an authenticated asset. Changing the type also requires changing the delivery URL, which can be done using the to_type parameter of the Rename method.

The access_mode or access_control parameter values can be modified to set an asset either as requiring authentication or as publicly available, without modifying the URL (the delivery type remains upload).

  • The access_control option additionally enables you to set an asset as authenticated except during a specified time period when the access will be publicly available.
  • The access_mode parameter can be retrieved or modified only using the Admin API. If your account has access_control enabled, you can also view or modify Access Control settings in the Media Library.

Note
The access control option is a Premium feature currently available only for accounts with a Custom plan and is enabled upon request.

In all cases, when an asset is set as requiring authentication, access to both the original asset URLs and all derived (transformed) versions of the asset are restricted. These assets and their derived versions cannot be accessed without some form of authentication. This authentication can take one of the following forms:

The basic features of each of these authentication options are summarized in the table below:

Signed URL Token Cookie
Time limited No Yes Yes
IP restriction No Yes Yes
User-session limited No No Yes
Pattern-based access (ACL) No Yes Yes
Customizable access per image No Yes Yes
CDN Caching Yes Yes1 Yes1
Plan availability Free Custom Custom
Setup required No Yes Yes
Private CDN distribution required No Yes Yes
CNAME required No No Yes

1 Only if using the authenticated delivery type for uploaded assets.

Signed-URL authentication

A signed Cloudinary delivery URL is a dynamic URL that has its signature validated before it becomes available for viewing (see the article about on-the-fly image manipulations secured with signed urls for more details).

A signed delivery URL contains a signature component in the format /s--SIGNATURE--/. The signature is generated from the first 8 characters of a base64 encoding of a SHA-1 (or SHA-256) digest of your asset public ID and transformation string concatenated with your API secret. The signature is automatically generated by Cloudinary's server-side SDKs by adding the sign_url boolean parameter to the helper method and setting it to true.

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.

For example, to create an image tag for the authenticated image sample cropped to a height and width of 300 pixels, while signing the transformation URL using the first 8 characters of the SHA-1 signature.

Ruby:
Copy to clipboard
cl_image_tag("sample-authenticated.jpg", :height=>300, :width=>300, :crop=>"scale", :sign_url=>true, :type=>"authenticated")
PHP:
Copy to clipboard
cl_image_tag("sample-authenticated.jpg", array("height"=>300, "width"=>300, "crop"=>"scale", "sign_url"=>true, "type"=>"authenticated"))
Python:
Copy to clipboard
CloudinaryImage("sample-authenticated.jpg").image(height=300, width=300, crop="scale", sign_url=True, type="authenticated")
Node.js:
Copy to clipboard
cloudinary.image("sample-authenticated.jpg", {height: 300, width: 300, crop: "scale", sign_url: true, type: "authenticated"})
Java:
Copy to clipboard
cloudinary.url().transformation(new Transformation().height(300).width(300).crop("scale")).signed(true).type("authenticated").imageTag("sample-authenticated.jpg");
.Net:
Copy to clipboard
cloudinary.Api.UrlImgUp.Transform(new Transformation().Height(300).Width(300).Crop("scale")).Signed(true).Action("authenticated").BuildImageTag("sample-authenticated.jpg")

Token-based authentication (premium feature)

The token-based authentication feature allows you to limit the validity of the asset delivery URL to a specific time frame. An authentication token is added as query parameters to the delivery URL, and is used to validate authentication before delivering the asset.

Setting up token-based authentication

This feature is available for accounts on the Advanced plan or higher, and requires setting up a private CDN distribution for your account on Cloudinary's side. Contact us to enable this feature. An encryption key will be created for your Cloudinary account that must then be used for generating the authentication tokens. Your Cloudinary account will also be set up with token-based authentication configured on the CDN.

Important
Before you start, make sure that you have set up your Framework to access Cloudinary's APIs (e.g., see the Java Integration documentation).

Delivering token-based authenticated media assets

The Cloudinary SDKs provide methods for creating delivery URLs (e.g., cl_image_tag and cl_video_tag in Rails). To create a token-based authentication URL for the asset, add the sign_url parameter (set to true) and the auth_token parameter to the SDK method. The auth_token parameter includes the following set of parameters:

  • key – (Required) - the token must be signed with the encryption key received from Cloudinary.
  • acl – (Optional) – an Access Control List for limiting the allowed URL path to a specified pattern (e.g., /video/authenticated/*). The pattern can include any of Cloudinary's transformations to also apply to the delivered assets. Note that if you add an overlay (e.g., for a watermark), you should also include the fl_layer_apply flag to ensure the layer cannot be modified. This parameter is useful for generating a token that can be added to a number of different URLs that share a common transformation. Without this parameter, the pattern defaults to the full URL path of the requested asset.
  • ip – (Optional) - only this IP address can access the resource.
  • start_time - (Optional) - timestamp of the UNIX time when the URL becomes valid. Default value: the current time.
  • duration – (Optional)1 – the duration that the URL is valid in seconds (counted from start_time).
  • expiration - (Optional)1 - timestamp in UNIX time when the URL expires.

1 Either duration or expiration must be provided (if both are provided, duration is ignored).

Example 1: To create a token-based authentication URL that allows access to the sample authenticated image for 5 minutes and is signed with 'MyKey':

Ruby:
Copy to clipboard
cl_image_tag("sample.jpg", :sign_url => true, :auth_token => { :key => "MyKey", :duration => 300 }, 
  :type => "authenticated")
PHP:
Copy to clipboard
cl_image_tag("sample.jpg",  array("auth_token" => array("key" => "MyKey", "duration" => 300), "type" => "authenticated",  
  "sign_url" => true));
Python:
Copy to clipboard
cloudinary.CloudinaryImage("sample.jpg").image(type = "authenticated", auth_token = dict(key = "MyKey", duration = 300),
  sign_url = True)
Node.js:
Copy to clipboard
cloudinary.image("sample.jpg",  { type: "authenticated",  auth_token: {key: "MyKey", duration: 300 },
  sign_url: true })
Java:
Copy to clipboard
cloudinary.url().transformation(new Transformation().type("authenticated").authToken(new AuthToken("MyKey").duration(300)).
  signed(true)).imageTag("sample.jpg");

Example 2: To create a token-based authentication URL that allows access to the dog authenticated video until 1/1/2018 (1514764800 in UNIX time) and is signed with 'MyKey':

Ruby:
Copy to clipboard
cl_video_tag("dog.mp4", :sign_url => true, :auth_token => { :key => "MyKey", :expiration => 1514764800 }, 
  :type => "authenticated")
PHP:
Copy to clipboard
cl_video_tag("dog.mp4",  array("auth_token" => array("key" => "MyKey", "expiration" => 1514764800), 
  "type" => "authenticated", "sign_url" => true));
Python:
Copy to clipboard
cloudinary.CloudinaryImage("dog.mp4").video(type = "authenticated", auth_token = dict(key = "MyKey", 
  expiration = 1514764800), sign_url = True)
Node.js:
Copy to clipboard
cloudinary.video("dog.mp4",  { type: "authenticated",  auth_token: {key: "MyKey", expiration: 1514764800 },
  sign_url: true })
Java:
Copy to clipboard
cloudinary.url().transformation(new Transformation().type("authenticated").authToken(new 
  AuthToken("MyKey").expiration(1514764800)).signed(true)).videoTag("dog.mp4");

Cookie-based authentication (premium feature)

The cookie-based authentication feature allows you to limit the delivery of authenticated assets, so that only users with a valid cookie have access. The validity of the cookie can additionally be restricted in the following ways:

  • To a specific time frame or expiration date
  • To a specific IP address
  • To a specific user (session)
  • To a specific pattern ACL (Access Control List)

Setting up cookie-based authentication

This feature is available for accounts on the Advanced plan or higher, and also requires using a CNAME. An HTTPS-enabled CNAME also entails an additional cost. Contact us to enable this feature. An encryption key will be created for your Cloudinary account that must then be used for signing all authentication cookies. Your Cloudinary account will also be set up with cookie-based authentication configured on the CDN.

Important
Before you start, make sure that you have set up your framework to access Cloudinary's APIs (For details, see the relevant framework documentation in the Framework Integration section of this Help).

Creating the authentication cookie

An authentication cookie needs to be created for accessing authenticated assets with the generate_auth_token method that accepts the following variables:

  • key – (Required) - the cookie must be signed with the encryption key received from Cloudinary.
  • acl – (Required) – an Access Control List for limiting the allowed URL path (e.g., /image/authenticated/*). The URL path can include any of Cloudinary's transformations to also apply to the delivered assets. Note that if you add an overlay (e.g., for a watermark), you should also include the fl_layer_apply flag to ensure the layer cannot be modified. See examples below.
  • ip – (Optional) – a specific IP Address that can access the authenticated assets.
  • duration – (Optional)1 – the duration that the cookie is valid in seconds. This value should be set reasonably, making sure to update the cookie as frequently as needed according to the desired session (e.g., matching the session expiration).
  • expiration - (Optional)1 - timestamp in UNIX time when the cookie will expire.
  • start_time - (Optional) - timestamp in UNIX time when the cookie becomes valid. Default value: the current time.

1 Either duration or expiration must be provided (if both are provided, duration is ignored).

Example 1: To create a cookie that is valid for 5 minutes, restricted to the IP Address 111.222.111.222, allows access to all authenticated images, and is signed with MY_KEY:

Copy to clipboard
 tokenOptions  =  { 
        :key => "MY_KEY", 
        :duration => 300, 
        :acl => "/image/authenticated/*",
        :ip => "111.222.111.222"
 }
 cookieToken = Cloudinary::Utils.generate_auth_token(tokenOptions)

Example 2: For allowing access only to authenticated videos that are delivered with a specific named transformation (t_authorized):

Copy to clipboard
:acl => "/video/authenticated/t_authorized/*"

The named transformation may add a watermark with the name of the employee, group or role who is accessing the image. In this case, a named transformation should be created via the management console for every user, group or role (respectively).

Example 3: For allowing access only when the name of the user is applied as a text overlay on the image:

Copy to clipboard
:acl => "/image/authenticated/l_text:Arial_45_bold:" + current_user + ",co_white/fl_no_overflow,fl_layer_apply/*"

So the following URL will be allowed (assuming the current user is John Smith):

Copy to clipboard
https://res.cloudinary.com/demo/image/authenticated/l_text:Arial_45_bold:John Smith,co_white/fl_no_overflow,fl_layer_apply/sample.jpg

But if a different user's name is applied, it won't match the cookie contents and will be unauthorized.

Cookie placement

The cookie token returned from the generate_auth_token method is a string that includes the cookie name (always __cld_token__) and its value, for example:

Copy to clipboard
__cld_token__=ip=111.222.111.222~exp=1512982559~acl=%2fimage%2fauthenticated%2f%2a~hmac=b17360091889151e9c2e2a7c713a074fdd29dc4ef1cc2fb897a0764664f3c48d

Set the cookie on your main domain, e.g., app.customer.com or on the sub-domain that points to Cloudinary, e.g., images.app.customer.com.

Domain blacklisting or whitelisting (Premium feature)

You can submit a request to the Cloudinary support team to enable only certain domains access to your delivered assets, or conversly, you can request to block certain domains from accessing your delivered assets.

Both of these options are supported only for Cloudinary accounts on an Advanced plan or higher with a custom CNAME.

Keep in mind that if you choose the whitelist option, you must supply Cloudinary support with a complete list of every domain to allow. For example, if you want to enable Facebook (or any other social network) to fetch your images or videos, make sure to include their domain/s in the whitelist you supply.

With either of these options, the domains that you allow (via whitelist or those not specified in your blacklist) will still be limited as usual by any of the access control features described on this page.

✔️ Feedback sent!