Secure Token

Token authentication ensures that a URL is only accessible during a defined Unix time. You can define the time frame with an exact expiration time. Once a token has expired, it is not possible anymore to access the content. Several tokens with different expiration times can be created for the same file. A 403 Forbidden will be return if the token is not valid and a 410 Gone if the secure token has already expired.


Setup Token Authentication

  1. Create a push or pull zone. If you use a pull zone for token authentication, make sure the origin URL is not public and it can’t be easily guessed.
  2. Enable Secure Token in zone settings.
  3. Define a Secure Token Key. This key will be needed to generate the token.
  4. Use one of the following code examples to generate the token (e.g. from your website).


	$secret = 'securetokenkey';
	$path = '/path/to/file1.jpg';

	// Expiration in seconds (e.g. 90 seconds)
	$expire = time() + 90; 

	// Generate token
	$md5 = md5($path.$secret.$expire, true);

	$md5 = base64_encode($md5);
	$md5 = strtr($md5, '+/', '-_');
	$md5 = str_replace('=', '', $md5);  

	// Use this URL
	$url = "{$path}?token={$md5}&expire={$expire}"; 

	echo $url;


import base64
from hashlib import md5
from time import time

secret = 'your_secret'
path = "/path/to/file1.jpg"

# expiration in seconds (e.g. 180 seconds)
expire = int(time()) + 180

# generate token
token = base64.encodestring(
                                    "%s%s%s" % (path, secret, expire)
                            ).replace("\n", "").replace("+", "-").replace("/", "_").replace("=", "")
secured_url = "" % (path, token, expire)

# return secured URL
print secured_url


require 'digest/md5'
require 'base64'

secret = 'your_secret'
path = '/path/to/your/file.jpg'

# expiry time in seconds (e.g. 3600 seconds)
expire = + 3600
token = Base64.encode64(
                        ).gsub("\n", "").gsub("+", "-").gsub("/", "_").gsub("=", "")

# final secured URL
url = "{path}?token=#{token}&expire=#{expire}"

puts url


var crypto = require('crypto'),
    secret = 'your_secret',
    path = '/path/to/your/file.jpg';

// define expiry (e.g. 120 seconds)
var expire = Math.round( + 120;

// generate md5 token
var md5String = crypto
  .update(path + secret + expire)

// encode and format md5 token
var token = new Buffer(md5String, 'binary').toString('base64');
token = token.replace(/\+/g, '-').replace(/\//g, '_').replace(/\=/g, '');

// return secure token
console.log('' + path + '?token=' + token + '&expire=' + expire);


import java.util.Date;
import org.apache.commons.codec.binary.Base64;

public class getSecureURL {

    // generate token
    private static String getBinaryToken(String path, String secret, Long expire) throws UnsupportedEncodingException, NoSuchAlgorithmException{
        String urlData = path + secret + expire.toString();
        MessageDigest md = MessageDigest.getInstance("MD5");
        byte[] messageDigest = md.digest(urlData.getBytes());
        String token = Base64.encodeBase64URLSafeString(messageDigest);
        return token;

    // main method
    public static void main(String[] args) throws Exception{
        String secret = "your_secret";
        String path = "/path/to/your/file.jpg";
        Date date = new Date();
        // expiry time (e.g. 300 seconds)
        Long expire = (date.getTime()/1000) + 300;
        String token = "" ;
        try {
            md5 = getBinaryToken(path, secret, expire);

            // final secured URL with token and expire variables
            String url = "" + path + "?token=" + md5 + "&expire=" + expire.toString() ;
            System.out.println("genrated url : " + url);
        } catch (Exception e){



Example of how the link will look like:

Generate a Secure Token with openssl

echo -n '{path}{secret}{timestamp}' | openssl md5 -binary | openssl base64 | tr +/ -_ | tr -d =


echo -n '/path/to/file1.jpgmysecret1384719072' | openssl md5 -binary | openssl base64 | tr +/ -_ | tr -d =


        1. KeyCDN

          We currently don’t have an ETA for this feature. How about using Hotlink Protection ( We will announce any new feature in our blog and on Twitter.

    1. Sven

      Secure token restricts the access based on the query string parameters. Blocking access by IP would require a custom rule. Please open a support request in that case.

      1. Laci

        Actually, I wanted to know if the IP can be encoded into the query string. I’m looking for a replacement for Cloudfront and there both the access time and the client’s IP can be included into the token.

  1. Andreas Schuldei

    i see you use md5. here it says there are collision attacks.

    “Also in 2004 more serious flaws were discovered in MD5, making further use of the algorithm for security purposes questionable; specifically, a group of researchers described how to create a pair of files that share the same MD5 checksum.[6][7] Further advances were made in breaking MD5 in 2005, 2006, and 2007.[8] In December 2008, a group of researchers used this technique to fake SSL certificate validity.[9][10] As of 2010, the CMU Software Engineering Institute considers MD5 “cryptographically broken and unsuitable for further use”,[11] and most U.S. government applications now require the SHA-2 family of hash functions.[12] In 2012, the Flame malware exploited the weaknesses in MD5 to fake a Microsoft digital signature.”

    can you please provide another, better cypher?

  2. Andreas Schuldei

    What is the recommended way of delivering part of your content with secure token and some without? have two different zones?

  3. Oleg Makogon

    I use Nimble media server as origin live HLS.
    After enabling Secure Token, my live stream stopped playing. I can download playlist.m3u8, i.e., Secure Token authorizes access to it.

    playlist.m3u8 has the contents:

    And player reports this error:
    access_http error: error: HTTP/1.1 403 Forbidden
    main error: open of `′ failed

    Is there a way to play the stream, for which playlist.m3u8 has such content?

  4. Batiste Bieler

    I cannot get it work with any query parameters: eg.

    This fails to authenticate

  5. Alan

    In the node.js example the step with Buffer is actually unnecessary. You can simply use `digest(‘base64’)` directly and skip converting it to binary and then feeding it into a buffer to then convert it to base64.

  6. Lukas

    for the expiration time, what time zone needs to be used (local. GMT, UTC)? also, with or without daylight saving?
    at the moment, if i want a 10 second expiration, i have to use -3590 seconds. server based in switzerland.
    thanks for some insights.

Leave A Comment?