Google Cloud CDN URLPrefix签名URL生成失败求助
Let's break down the issues in your implementation and fix them step by step—since your single-file signed URLs work, we know the core setup (backend bucket, CDN config, keys) is valid, so the problem is in how you're generating the prefix-signed URLs.
Key Issues in Your Current Code
1. Using HTML-escaped & Instead of Raw & in Signature Payload
Google Cloud CDN expects the signature to be computed against a string with raw & separators between parameters, not HTML-escaped &. When you sign a string with &, the resulting signature won't match what the CDN calculates when it parses the actual URL parameters.
2. Incorrect Expires Timestamp Format
Your code uses new Date().getTime() + opts.expires which returns a millisecond timestamp, but Cloud CDN requires the Expires parameter to be a Unix timestamp in seconds. Using milliseconds will set an invalid expiration time (far in the future, which might be treated as invalid).
3. Potential URLPrefix Mismatch
Ensure the URLPrefix value exactly matches the prefix of the request URL (including protocol, domain, and path). For example, if you're requesting https://my-server/sample/360p/video.mp4, the URLPrefix should be https://my-server/sample/360p/ (or https://my-server/sample/360p—trailing slash is optional but must align with your request path).
Fixed Implementation
Here's the corrected signCdnUrl3 function addressing all the above issues:
import { createHmac } from 'crypto'; import Base64urlUtil from './Base64urlUtil'; // Adjust import path as needed export interface SignedUrlOptions { baseUrl: string; keyName: string; keyBase64: string; expires: number; // Expiration time in milliseconds (e.g., 3600000 for 1 hour) } export function signCdnUrl3(fileName: string, opts: SignedUrlOptions, urlPrefix?: string) { // Calculate Unix timestamp in seconds (not milliseconds) const expireVal = Math.floor((Date.now() + opts.expires) / 1000).toString(); let paramsToSign: string; if (urlPrefix) { const urlPrefixCombined = `${opts.baseUrl}${urlPrefix}`; const urlPrefixEncoded = Base64urlUtil.encode(urlPrefixCombined); // Use raw & separators, not & paramsToSign = `URLPrefix=${urlPrefixEncoded}&Expires=${expireVal}&KeyName=${opts.keyName}`; } else { // Fallback to single-file signing if no prefix is provided const urlToSign = `${opts.baseUrl}/${fileName}?Expires=${expireVal}&KeyName=${opts.keyName}`; paramsToSign = urlToSign.split('?')[1]!; } // Compute signature const keyBuffer = Buffer.from(opts.keyBase64, 'base64'); let signature = createHmac('sha1', keyBuffer).update(paramsToSign).digest('base64'); signature = Base64urlUtil.escape(signature); // Build the final URL with raw & separators const finalUrl = `${opts.baseUrl}/${fileName}?${paramsToSign}&Signature=${signature}`; return finalUrl; }
Additional Troubleshooting Steps
- Verify Signature Payload: To debug, log the
paramsToSignstring before signing it. Compare it against what Cloud CDN expects: it should be exactlyURLPrefix=<base64url-encoded-prefix>&Expires=<unix-seconds>&KeyName=<your-key-name>. - Check Load Balancer Logs: Look for the specific error in the load balancer logs (under Cloud Logging > Cloud Load Balancing > HTTP(S) Load Balancers). Common errors include:
signature_invalid: The signature doesn't match the payload (most likely due to incorrect&usage or timestamp format).signature_expired: TheExpirestimestamp is in the past (confirm your server's clock is synchronized).
- Validate URLPrefix Encoding: Cross-check your encoded prefix with standard base64url rules—ensure there are no padding characters (
=) in the final encoded string (yourBase64urlUtil.escapemethod already handles this correctly). - Test with a Fixed Expiration: Hardcode a future Unix timestamp (e.g.,
1735689600for January 1, 2025) to rule out timestamp calculation issues.
内容的提问来源于stack exchange,提问作者Robert Field

