ImageKit provides some security features for media delivery on all pricing plans, including free plans. These features help you prevent unauthorized access and modification of your media assets. However, to prevent hotlinking and advanced restrictions based on HTTP referrer, IP, country, etc., you should use advanced security features available on the enterprise plan.
In this guide, we will cover the following basic security features:
Restrict unsigned URLs
Signed URLs have a signature that is generated using your private API key and the URL. This signature is then appended to the URL as a query parameter. When the URL is requested, ImageKit verifies the signature to ensure that the URL has not been tampered with. Besides signature verification, you can optionally specify the expiration time for the signed URL. After the expiry time, the signed URL will no longer be valid. This allows you to:
- Enforce time-bound access to your media assets. After the expiration time, the signed URL will no longer be valid.
- Prevent unauthorized modification of the URL. For example, you are using image overlay to add a watermark to protect the artwork. If the signed URL is tampered with to remove the watermark transformation, ImageKit will not serve the image.
You can restrict access to your media assets by enforcing signed URLs from the dashboard independently for images and videos.
You have three options to enforce signed URLs:
- Do not restrict - ImageKit will serve the media assets even if the URL has no valid signature.
- Restrict only AI transformation requests - ImageKit will serve the media assets if the URL does not have a valid signature, except for AI transformation requests that consumes extension units. This is recommended if you use AI transformations to prevent unauthorized consumption of extension units on your account.
- Restrict all requests - ImageKit will only serve the media assets if the URL has a valid signature. If the URL does not have a valid signature, ImageKit will return a
401 Unauthorized
status code.
When signed URLs are enforced, ImageKit will only serve the media assets if the URL has a valid signature. When signed URLs are not enforced, ImageKit will serve the media assets even if the URL does not have a valid signature.
Note: Signed URLs are not enforced by default. You need to enable this feature from the dashboard. This feature only works for images and videos. For any other resource type, you need to mark the file as private.
How to generate signed URLs
A signed URL has a ik-s
query parameter that contains the signature. The signature is generated using your private API key and the URL. Besides the signature, you can optionally specify the expiry time using the ik-t
query parameter.
A typical signed URL looks like this:
signature ┌──────────────────────────────────────┐ {url-endpoint}/image.jpg?ik-s=e26ca157df99b30b2443d7cb6886fc396fb4c87b&ik-t=9999999999 └────┬───┘ expiry time
- signature -
ik-s
query parameter contains the signature generated using your private key and the URL. ImageKit uses this signature to verify the authenticity of the URL. If the signature is invalid, ImageKit will return the401 Unauthorized
status code when the restricted unsigned URL setting is enabled. - expiry time - This is optional. The
ik-t
query parameter contains the expiry time, which is the number of seconds after epoch time (UTC) when the signed URL will expire. After this time, the signed URL will no longer be valid. ImageKit will return a401 Unauthorized
status code if the signed URL is accessed after the expiry time.
Here is this pseudo code to generate signed URLs. To simplify things, we have added utility methods in all our server-side SDKs to generate signed URLs. The SDKs handle edge cases like URL encoding for you.
var imageURL = imagekit.url({ path : "/default-image.jpg", queryParameters : { "v" : "123" }, transformation : [{ "height" : "300", "width" : "400" }], signed : true, expireSeconds : 300 });
image_url = imagekit.url({ "path": "/default-image", "query_parameters": { "v": "123" }, "transformation": [{ "height": "300", "width": "400" }], "signed": True, "expire_seconds": 300 });
$imageURL = $imageKit->url(array( "path" => "/default-image.jpg", "queryParameters" => array( "v" => "123", ), "transformation" => array( array( "height" => "300", "width" => "400" ), ), "signed" => true, "expireSeconds" => 300, ));
List<Map<String, String>> transformation=new ArrayList<Map<String, String>>(); Map<String, String> scale=new HashMap<>(); scale.put("height","600"); scale.put("width","400"); transformation.add(format); Map<String, Object> options=new HashMap(); options.put("path","/default-image.jpg"); options.put("signed",true); options.put("expireSeconds",300); String url = ImageKit.getInstance().getUrl(options);
imagekitio = ImageKitIo::Client.new("your_private_key", "your_public_key", "your_url_endpoint") image_url = imagekitio.url({ path: "/default-image", query_parameters: { "v": "123" }, transformation: [{ height: "300", width: "400" }], signed: True, expire_seconds: 300 })
Psuedo code to generate signed URL
To generate the signed URL yourself, refer to the pseudo-code below.
The value of the signature ik-s
should be lowercase.
// Assume we have an image URL var imageUrl = "https://ik.imagekit.io/your_imagekit_id/tr:w-400:rotate-91/sample/testing-file.jpg"; // This is our endpoint var urlEndpoint = "https://ik.imagekit.io/your_imagekit_id"; // Make sure urlEndpoint has a trailing slash (/) if(urlEndpoint[urlEndpoint.length - 1] != "/") { urlEndpoint = urlEndpoint + "/" } // Let's say we want to expire image in 300 seconds, so expireTimestamp (UTC timestamp) would be var expiryTimestamp = parseInt(new Date().getTime() / 1000, 10) + 300; // Remove the urlEndpoint from image URL var str = imageUrl.replace(urlEndpoint,""); // str will be tr:w-400:rotate-91/sample/testing-file.jpg // Append the expiryTimestamp in above str str = str + expiryTimestamp // str will be tr:w-400:rotate-91/sample/testing-file.jpg9999999999 // Calcualte the signature using your priviate key var signature = crypto.createHmac('sha1', "your_private_key").update(str).digest('hex'); // Add ik-t and ik-s query parameters in the url var finalImageUrl = imageUrl + "?ik-t=" + expiryTimestamp + "&ik-s=" + signature;
Most browser engines encode special characters, diacritics, or characters from different charsets to UTF-8. For example, é
(diacritic) is encoded as e%CC%81
, and so on.
To ensure that your signed URLs that contain such characters work, you must encode the complete URL of the input image before signing it. For example, instead of using /default-image-with-é.jpg
as the input path for the signed URL, use /default-image-with-e%CC%81.jpg
. The backend SDKs handle this encoding for you.
Restrict unnamed transformations
Transformation allows you to resize, crop, rotate, and apply various effects to your images and videos. Transformations are ultimately a few URL parameters that you append to the URL of the media asset. For example, to resize an image to 300x400, you can append tr:w-300,h-400
to the URL.
To prevent unauthorized manipulation of your media assets, you can create named transformations in the dashboard and enforce restrictions on the usage of unnamed transformations. When you enforce this setting, ImageKit will only serve the media asset if the transformation is named. If the transformation is not named, ImageKit will return a 400: Bad Request
status code.
You can turn on this setting separately for images and videos from the dashboard.
Private files
If you do not want to restrict unsigned URLs on all your media assets but still want to prevent unauthorized access to some of them, you can mark them as private. When a resource is private, it can only be accessed using a valid signed URL or using a valid named transformation. This is useful when you want to share media assets with only authenticated users or after a certain event, like payment completion.
Here is how to mark a resource as private:
- ImageKit Media Library - You can mark a file as private during upload from the dashboard or through upload API (
isPrivateFile
property). Once a file is marked private, its status cannot be changed. To unmark a file as private, re-upload the file with the same name as the one marked private. - Amazon Simple Storage Service (S3) - If you are delivering files from your S3 bucket added as an external storage, set the object metadata
x-amz-meta-isprivatefile
astrue
to mark the file as private. This documentation from AWS provides an overview of adding metadata to an object within S3. - Google Cloud Storage (GCS) - If you are delivering files from your GCS bucket added as an external storage, set an object metadata
isprivatefile
astrue
to mark the file as private. This documentation from GCS provides an overview of adding metadata to an object within Google Cloud Storage. - HTTP server/Web server - If you are delivering files from an HTTP server added as an external storage, add a response header
Is-Private-File: true
. When ImageKit sees this header, it will consider the file as private.