A Beginner's Guide to Cloud CDN Signed URLs

The Digital "VIP Ticket" for Your Private Content.

The Concept: Protecting Private Content

Cloud CDN is great for delivering public content like logos and style sheets. But what about content that should only be seen by specific, authenticated users? For example:

If you put this content in a public Cloud Storage bucket, anyone with the link can access it. Signed URLs are the solution to this problem.

The VIP Concert Ticket Analogy

Think of your private content as a VIP concert. A normal URL is like an address to the venue—anyone who has it can show up.

A **Signed URL** is like a special, non-transferable VIP ticket. It has three key properties:

  1. It grants access to a specific resource: The ticket is only for this specific concert (`/videos/course101.mp4`).
  2. It has an expiration date: The ticket is only valid for a short time (e.g., the next 10 minutes). After that, it's useless.
  3. It has a verifiable signature: The ticket has a holographic seal (a cryptographic signature) that the bouncer at the door can instantly verify was issued by an official ticket agent (your application). It cannot be forged.

The Core Idea: A Signed URL is a special, temporary URL that grants a user time-limited access to a private resource. It's generated on-the-fly by your application and "signed" with a secret key that only you and Google know.

How It Works: A Two-Phase Process

Using Signed URLs involves two distinct phases: first, your application must generate the URL, and second, the user's browser uses it to access the content.

Phase 1: Your Application Generates the Signed URL

The user never signs the URL themselves. They access your web application, which verifies they are logged in and have permission to see the content. If they do, your application server generates the special URL.

sequenceDiagram participant User as User's Browser participant WebApp as Your Web Application Server participant SecretKey as Secret CDN Key
(Stored securely by your app) User->>WebApp: 1. "I'm logged in and I'd like to
watch my purchased video." WebApp->>WebApp: 2. Check user's permissions.
OK! WebApp->>SecretKey: 3. Use the secret key to generate a
secure signature for the video URL. WebApp-->>User: 4. "Here is your temporary, personal ticket
to access the video." (Sends signed URL)

Phase 2: The User Accesses the Content via the CDN

The user's browser then uses this special, long URL to request the content. Google's Edge Cache intercepts the request and performs a series of checks.

sequenceDiagram participant User as User's Browser participant EdgeCache as Google Edge Cache participant Origin as Your Private Origin Server User->>EdgeCache: 1. Request content using the Signed URL. EdgeCache->>EdgeCache: 2. Check the "VIP Ticket".
IF the signature is valid AND
the expiration time has not passed... alt Ticket is Valid (The "Then" block) EdgeCache-->>User: 3. Serve the private video.
(May serve from cache or fetch from origin). else Ticket is Invalid (The "Else" block) EdgeCache-->>User: 3. DENY. Return a 403 Forbidden error. end

Key Terms and Acronyms

TermWhat It Means
Signed URL A URL with extra query parameters for an expiration time and a cryptographic signature that grants temporary access.
Origin Your source server (e.g., a private Cloud Storage bucket) that holds the actual content.
Signed URL Key A secret, random string of text that you generate. Your application uses this key to create the signature. You must keep this key private.
Signature The unique, tamper-proof "holographic seal" attached to the URL, created using your secret key.

How to Configure Signed URLs: A Step-by-Step Guide

This process requires you to have an existing Global External Application Load Balancer with Cloud CDN already enabled on its backend service.

Step 1: Create a Signed URL Key

First, we need to generate the secret key. This command creates a new, random key and saves it to a file on your local machine.

CRITICAL: Treat this key file like a password or a private SSH key. It must be stored securely and made available to your application server. Do not commit it to a public source code repository.

# Create a file to store the new key
head -c 16 /dev/urandom | base64 | tr -d '+/=' > cdn-key-file.txt

# Now, create the key within Cloud Armor using that file.
gcloud compute security-policies signed-url-keys create my-cdn-key \
    --key-name=my-key-name \
    --key-file=cdn-key-file.txt \
    --project=my-gcp-project-12345

Step 2: Add the Key to Your Backend Service or Bucket

Now, you must "tell" your backend that it should expect and validate URLs signed with this specific key.

# This command updates your backend service to use the new key
gcloud compute backend-services update my-cdn-backend-service \
    --signed-url-cache-max-age=3600 \
    --signed-url-key-name=my-key-name \
    --global

Step 3: Generate a Signed URL (Simulated)

In a real application, your server-side code (in Python, Go, Node.js, etc.) would perform this step. However, for testing purposes, `gcloud` provides a handy command to simulate this process.

The URL you use here should be the public-facing URL of your load balancer, not the private address of your origin.

# This command simulates what your application server would do
gcloud compute sign-url \
    "https://www.example.com/videos/private/course101.mp4" \
    --key-name=my-key-name \
    --key-file=cdn-key-file.txt \
    --expires-in=10m

The command will output a very long URL containing `Expires`, `KeyName`, and `Signature` parameters. This is your temporary VIP ticket.

Step 4: Verifying It Works

  1. Test the Signed URL: Copy the long URL generated in the previous step and paste it into your browser or use `curl`. You should successfully see or download the content.
  2. Test the Original URL: Try to access the original, unsigned URL (e.g., `https://www.example.com/videos/private/course101.mp4`). You should receive a `403 Forbidden` or similar error.
  3. Test an Expired URL: Wait for the expiration time (10 minutes in our example) to pass and try the signed URL again. You should now receive an error, proving that the time limit works.