Open the CDN zone → Settings tab in my.cubepath.com.

Custom domain

Every zone gets a system-generated domain (<name>.cubecdn.io) that works immediately. To serve traffic on your own hostname instead:

  1. 1
    Point a CNAME at the zone
    In your DNS, create a CNAME from your hostname (e.g. cdn.example.com) to the zone's system domain.
  2. 2
    Set the custom domain
    Enter the hostname in Custom Domain and save.
  3. 3
    Wait for SSL
    A certificate is requested automatically once the CNAME resolves.

SSL

SSL Status shows Valid, Pending, Error or Not configured. Two types are available:

  • Automatic (Let's Encrypt) — free, provisioned and renewed for you. The right choice unless you have a specific reason not to use it.
  • Custom Certificate — pick one of your own from SSL Certificates, or upload a new one. Available on higher plans, and needed for EV certificates or a certificate that must match one you use elsewhere.

Signed URLs (Token Auth)

Token Auth serves the zone's content only to people you authorize. Every link you publish carries a cryptographic signature and an expiry; anyone requesting without a valid signed URL gets 403 Forbidden.

It's the standard pattern for paid video, gated downloads, private galleries, time-limited shares, and for keeping content out of search engines and away from hotlinkers.

How it works

When you enable Token Auth, CubePath generates a shared secret for the zone. Your backend computes an HMAC-SHA256 over the path plus an expiry timestamp plus the secret, base64url-encodes it, and appends ?token=...&expires=... to the URL. The edge recomputes the same HMAC with its copy of the secret and compares: match, we serve; mismatch, 403. The secret itself never travels in the URL.

Enable it

Open the Signed URLs (Token Auth) card and toggle Enable Token Auth. The secret is generated and shown once.

Sign URLs in your backend

The math is HMAC-SHA256(secret, path + str(expires)), base64url-encoded without padding.

import hmac, hashlib, base64, time

SECRET = "<your_zone_secret>"

def sign(path: str, ttl_seconds: int = 3600) -> str:
    expires = int(time.time()) + ttl_seconds
    msg = f"{path}{expires}".encode()
    mac = hmac.new(SECRET.encode(), msg, hashlib.sha256).digest()
    token = base64.urlsafe_b64encode(mac).rstrip(b"=").decode()
    return f"https://your-zone.cubecdn.io{path}?token={token}&expires={expires}"
const crypto = require('crypto');
const SECRET = process.env.CDN_SECRET;

function sign(path, ttlSeconds = 3600) {
  const expires = Math.floor(Date.now() / 1000) + ttlSeconds;
  const mac = crypto.createHmac('sha256', SECRET).update(`${path}${expires}`).digest();
  const token = mac.toString('base64url').replace(/=+$/, '');
  return `https://your-zone.cubecdn.io${path}?token=${token}&expires=${expires}`;
}
function sign($path, $ttlSeconds = 3600) {
    $secret = getenv('CDN_SECRET');
    $expires = time() + $ttlSeconds;
    $mac = hash_hmac('sha256', $path . $expires, $secret, true);
    $token = rtrim(strtr(base64_encode($mac), '+/', '-_'), '=');
    return "https://your-zone.cubecdn.io{$path}?token={$token}&expires={$expires}";
}

The Settings card also has a Generate signed URL subform: paste a path, set a validity window, and get a ready-to-use URL. It's for testing and one-off shares — production traffic should sign in your backend, with no API round-trip per URL.

Choosing an expiry

ValidityUse caseTrade-off
5 minutesOne-off downloads, secured API responsesRefreshing after the window means re-signing
1 hourVideo playback sessionsLong videos outlive it; the player must re-sign
24 hoursDaily content rotationA leaked URL works for a day
7 daysStatic cacheable media, app bundlesLong exposure if leaked; consider IP binding

Validity is capped at 7 days (604800 s) to limit the damage from a leak.

Bind tokens to the client IP

The Bind tokens to client IP toggle folds the request's source IP into the signature, so a leaked URL only works from the IP it was issued to. It's a strong defence against links being shared in chats and forums.

The cost is real: clients whose IP changes mid-session — mobile networks on CGNAT, hotel WiFi, VPNs, roaming between 4G and home WiFi — lose access mid-stream.

If you enable it, pass the client's public IP when signing (typically the first entry of X-Forwarded-For if you're behind a proxy). IPv6 must be in canonical compressed form (2001:db8::1, not [2001:db8::1]).

Rotating the secret

  1. 1
    Rotate
    Click Rotate secret and confirm.
  2. 2
    Copy the new secret
    Same one-shot reveal rule as the first time.
  3. 3
    Update your backend
    Replace CDN_SECRET and redeploy so the app picks it up.

Rotation is instant and retroactive: every URL signed with the old secret starts returning 403 immediately, and content being streamed at that moment breaks. Plan rotations for a low-traffic window, or keep expiry windows short so the disruption is small.

What Token Auth doesn't do

  • It isn't a WAF. No bot filtering, no rate limiting, no country blocking — that's WAF rules.
  • It isn't user authentication. It authorizes one URL against a shared secret; anyone holding that URL can use it, unless IP binding is on.
  • It doesn't encrypt content. Transport is HTTPS, but the bytes are the same ones in your origin.

Troubleshooting

SymptomLikely cause
403 on every URLToken Auth is on but requests carry no ?token=, or the token used the wrong secret
403 on some URLsThe signed path and the requested path differ — trailing slash, case, or percent-encoding. Sign and request the exact same string
403 right after rotatingURLs signed with the old secret. Re-sign them
Works on desktop, fails on mobileIP binding is on and the client changed network. Disable it or shorten the expiry
Random 403s during playbackThe URL expired mid-stream. Lengthen the expiry, or have the player fetch a fresh URL before it lapses

CORS

CORS (cross-origin fetch) lets JavaScript on other domains fetch content from this zone. You need it for HLS/DASH players, fetch() calls and any cross-domain XHR — without it the browser blocks the response even though the CDN served it correctly.

Enable it and set Allowed origins: either * for any origin, or a list of fully-qualified origins, one per line or comma-separated. Each must include the scheme (https://example.com) and no path.