CacheFly Signed URLs v2 (2.2.0)

Download OpenAPI specification:

This document covers the Alibaba implementation of CacheFly Signed URLs v2 only. Support for additional algorithms (CDN77, Bunny, Cloudflare, Searchbots, etc.) is coming soon; their v2 documentation will be published as they become available.

Introduction

Signed URLs restrict access to content by validating a signature contained in the request URL (path or query) before origin fetch. v2 introduces a structured configuration with a default policy, per-path exceptions, and a fallback mechanism.

Format

The v2 configuration file can be provided as YAML or JSON. The top-level structure contains a single default protection and a list of exceptions that can override it for matching requests.

Top-level structure

---
default:    # Required. The default protection applied to all requests.
  # ... a protection object (see below)
exceptions: # Required. Per-path overrides.
  - path: "/private"          # Request path must start with this (default "/")
    pathFilter: ["*"]          # Optional wildcard patterns to match the rest of the path
    extensions: ["*"]          # Optional file extensions to match
    # The protection applied when this exception matches
    algorithm: "alibaba"
    secret: "<shared secret>"
    type: "a"
    # ... other Alibaba options
  • default: A protection that applies when no exception matches.
  • exceptions[]: Ordered list; the first matching exception wins.
    • path: Request must start with this prefix to match.
    • pathFilter: Optional wildcard(s) applied to the remaining path.
    • extensions: Optional list of file extensions to match.

Protection object

A protection selects and configures the algorithm enforced for a given scope. All protections support:

  • algorithm: The algorithm name.
  • fallback: Another protection to attempt if this one denies.
  • denyCode: HTTP status code to return when denied (default 403; 4xx only).

Rollout status (v2): alibaba (available in this document); cdn77, bunny-basic, cloudflare, searchbots (coming soon). Generic allow/deny exist but are outside the scope of this document.

Processing flow

At request time, the CDN evaluates the configuration as follows:

  1. Start with default protection.
  2. Find the first matching exception (by path, then pathFilter/extensions) and adopt its protection if found.
  3. Apply the adopted protection:
    • If it returns allow, the request proceeds.
    • If it returns deny and a fallback is configured, recursively apply the fallback.
    • If all checks deny, return denyCode (default 403).

If your protection rewrites the request (e.g., removes signing tokens), the rewritten path is used for the remainder of the request lifecycle.

Alibaba algorithm

This algorithm implements the Alibaba Cloud CDN URL Signing types, with some configurable extensions.

Overview

  • algorithm: alibaba
  • Required: secret
  • Optional:
    • type: One of a, b, c, c1, c2, f, f1, f2, or auto (default auto).
    • ttl: Integer seconds (default 1800) added to the provided timestamp to determine expiry.
    • hash: One of md5, sha1, sha256, sha384, sha512 (default md5).
    • rewritePath: Boolean (default true) — if true, the request URI is rewritten to remove signing bits after verification.
    • signField: Query parameter name holding the signature (types C2/F2; defaults per type).
    • timeField: Query parameter name holding the timestamp (types C2/F2; defaults per type).
    • utcOffset: Integer hours offset applied to current time when evaluating expiry. Defaults per type (notably type B defaults to 8).
    • pathFormat: For path-based types: TS/SIG or SIG/TS (defaults per type).
    • timeFormat: One of decimal, hex, yyyyMMddHHmm (defaults per type).
    • signatureFormat: Internal signature template, see Signature construction below (defaults per type).

The secret must be 6–128 characters.

Supported types

  • Type A (query): ?auth_key=<timestamp>-<rand>-<uid>-<md5hex>

    • Defaults: signField=auth_key, timeFormat=decimal, utcOffset=0, signatureFormat=[P]-[T]-[R]-[I]-[S].
  • Type B (path): /<timestamp>/<md5hex>/path

    • Defaults: pathFormat=TS/SIG, timeFormat=yyyyMMddHHmm, utcOffset=8, signatureFormat=[S][T][P].
  • Type C (path or query):

    • Path (C1): /<md5hex>/<hex_ts>/path
    • Query (C2): ?KEY1=<md5hex>&KEY2=<hex_ts>
    • Defaults: signField=KEY1, timeField=KEY2, timeFormat=hex, pathFormat=SIG/TS, utcOffset=0, signatureFormat=[S][P][T].
  • Type F (path or query):

    • Path (F1): /<md5hex>/<hex_ts>/path
    • Query (F2): ?sign=<md5hex>&time=<hex_ts>
    • Defaults: signField=sign, timeField=time, timeFormat=hex, pathFormat=SIG/TS, utcOffset=0, signatureFormat=[S][P][T].
  • Type auto: Detects signing type from the request:

    • Query: auth_key → A, KEY1 → C, sign → F.
    • Path: /12-digit-ts/32-hex/ → B, /32-hex/8-hex/ → C (preferred over F when ambiguous).

If detection fails, the request is denied.

Timestamps and expiry

  • decimal: 10-digit Unix time (seconds).
  • hex: 8 hex digits representing Unix time (seconds).
  • yyyyMMddHHmm: 12-digit UTC clock representation (year, month, day, hour, minute). For type B, evaluation is performed relative to utcOffset (defaults to +8 hours).

Expiry check: parsed_timestamp + ttl >= now_with_offset must hold. Otherwise the request is denied.

Signature construction

The algorithm constructs a string and applies the configured hash:

  • Variables:
    • S: secret
    • T: timestamp (string form exactly as in the URL)
    • P: path (request URI without query)
    • Q: full request URI including query (after removing signing fields)
    • E: URL-encoded request URI including query (after removing signing fields)
    • I: user id (Type A)
    • R: nonce/random (Type A)

Default signatureFormat per type:

  • A: [P]-[T]-[R]-[I]-[S]
  • B: [S][T][P]
  • C/F: [S][P][T]

The output is the lowercase hexadecimal digest for the selected hash.

Rewrite behavior

If rewritePath: true (default):

  • Path types: the /<ts>/<sig>/ (or /<sig>/<ts>/) prefix is stripped from the request before origin fetch.
  • Query types: signing fields are removed from the query string.

Disable this only if your origin requires the signing bits.

Configuration examples (Alibaba)

Type A (query)

---
default:
  algorithm: deny

exceptions:
  - path: "/video"
    algorithm: alibaba
    secret: "your-secret-here"
    type: a
    ttl: 1800
    hash: md5
    # rewritePath: true   # default

Example request:

/video/file.mp4?auth_key=<ts>-<rand>-<uid>-<md5hex>

Type B (path)

---
default:
  algorithm: deny

exceptions:
  - path: "/downloads"
    algorithm: alibaba
    secret: "your-secret-here"
    type: b
    ttl: 900
    # timeFormat defaults to yyyyMMddHHmm; utcOffset defaults to 8

Example request (default TS/SIG):

/downloads/202503141230/0123456789abcdef0123456789abcdef/path/to/file.mp4

Type C (path and query)

---
default:
  algorithm: deny

exceptions:
  # C1: path variant
  - path: "/assets"
    algorithm: alibaba
    secret: "your-secret-here"
    type: c1
    # pathFormat defaults to SIG/TS; timeFormat defaults to hex

  # C2: query variant
  - path: "/public"
    algorithm: alibaba
    secret: "your-secret-here"
    type: c2
    signField: KEY1   # default
    timeField: KEY2   # default

Examples:

# C1 path
/assets/0123456789abcdef0123456789abcdef/5f5e1000/file.jpg

# C2 query
/public/file.jpg?KEY1=0123456789abcdef0123456789abcdef&KEY2=5f5e1000

Type F (path and query)

default:
  algorithm: deny

exceptions:
  - path: "/media"
    algorithm: alibaba
    secret: "your-secret-here"
    type: f2
    signField: sign     # default
    timeField: time     # default
    # signatureFormat: "[S][Q][T]"  # optional variant if needed

Examples:

# F1 path
/media/0123456789abcdef0123456789abcdef/5f5e1000/clip.mp4

# F2 query
/media/clip.mp4?sign=0123456789abcdef0123456789abcdef&time=5f5e1000

Signature generation examples

Below are minimal PHP reference implementations matching the defaults. Adjust hash to your chosen algorithm.

Type A (query) — PHP

function alibaba_type_a_sign(string $url, string $secret, string $uid = '0', ?string $rand = null, ?int $ts = null, string $hash = 'md5'): string {
    $parts = parse_url($url);
    $path = $parts['path'] ?? '/';
    $ts = $ts ?? time();
    $rand = $rand ?? bin2hex(random_bytes(4));
    $internal = $path . '-' . $ts . '-' . $rand . '-' . $uid . '-' . $secret;
    $signature = match ($hash) {
        'md5' => md5($internal, false),
        'sha1' => sha1($internal, false),
        'sha256', 'sha384', 'sha512' => hash($hash, $internal, false),
        default => md5($internal, false)
    };
    $token = $ts . '-' . $rand . '-' . $uid . '-' . $signature;
    $query = [];
    if (!empty($parts['query'])) { parse_str($parts['query'], $query); }
    $query['auth_key'] = $token;
    $query_str = http_build_query($query);
    return ($parts['scheme'] ?? 'https') . '://' . ($parts['host'] ?? '') . $path . ($query_str ? ('?' . $query_str) : '');
}

Type B (path) — PHP

function alibaba_type_b_path(string $base, string $path, string $secret, string $hash = 'md5', int $utcOffset = 8): string {
    $path = '/' . ltrim($path, '/');
    $ts = gmdate('YmdHi', time() + ($utcOffset * 3600)); // yyyyMMddHHmm in UTC+offset
    $internal = $secret . $ts . $path; // [S][T][P]
    $signature = match ($hash) {
        'md5' => md5($internal, false),
        'sha1' => sha1($internal, false),
        'sha256', 'sha384', 'sha512' => hash($hash, $internal, false),
        default => md5($internal, false)
    };
    return rtrim($base, '/') . '/' . $ts . '/' . $signature . $path; // TS/SIG
}

Type C/F (query) — PHP

function alibaba_type_cf_query(string $url, string $secret, string $signField = 'KEY1', string $timeField = 'KEY2', string $hash = 'md5', ?int $ts = null): string {
    $parts = parse_url($url);
    $path = $parts['path'] ?? '/';
    $ts = $ts ?? time();
    $hexTs = str_pad(dechex($ts), 8, '0', STR_PAD_LEFT);
    $internal = $secret . $path . $hexTs; // [S][P][T]
    $signature = match ($hash) {
        'md5' => md5($internal, false),
        'sha1' => sha1($internal, false),
        'sha256', 'sha384', 'sha512' => hash($hash, $internal, false),
        default => md5($internal, false)
    };
    $query = [];
    if (!empty($parts['query'])) { parse_str($parts['query'], $query); }
    $query[$signField] = $signature;
    $query[$timeField] = $hexTs;
    $query_str = http_build_query($query);
    return ($parts['scheme'] ?? 'https') . '://' . ($parts['host'] ?? '') . $path . ($query_str ? ('?' . $query_str) : '');
}

Type C/F (path) — PHP

function alibaba_type_cf_path(string $base, string $path, string $secret, string $hash = 'md5', string $pathFormat = 'SIG/TS'): string {
    $path = '/' . ltrim($path, '/');
    $ts = time();
    $hexTs = str_pad(dechex($ts), 8, '0', STR_PAD_LEFT);
    $internal = $secret . $path . $hexTs; // [S][P][T]
    $signature = match ($hash) {
        'md5' => md5($internal, false),
        'sha1' => sha1($internal, false),
        'sha256', 'sha384', 'sha512' => hash($hash, $internal, false),
        default => md5($internal, false)
    };
    $base = rtrim($base, '/');
    if ($pathFormat === 'TS/SIG') {
        return $base . '/' . $hexTs . '/' . $signature . $path;
    }
    // Default SIG/TS
    return $base . '/' . $signature . '/' . $hexTs . $path;
}

Troubleshooting tips

  • Ensure your secret length is between 6 and 128 characters.
  • Verify that your timestamp format matches the configured timeFormat.
  • For path types, compute the signature over the remaining path after the /<ts>/<sig>/ (or /<sig>/<ts>/) prefix.
  • Use lowercase hexadecimal digests for all hash types.
  • When using type: auto, confirm that your URL layout matches the detection heuristics.

Notes on other algorithms

This document is Alibaba-only for v2. Support and documentation for cdn77, bunny-basic, cloudflare, and searchbots will be published soon. For immediate needs, refer to the v1 documentation (signedurlsv1.md) or contact support.

CacheFly
Documentation