Sign requests
Verify a signed request using the HMAC and SHA-256 algorithms or return a 403.
The following Snippet will:
- 
For request URLs beginning with /generate/, replace/generate/with/, sign the resulting path with its timestamp, and return the full, signed URL in the response body.
- 
For all other request URLs, verify the signed URL and allow the request through. 
export default {  async fetch(request) {    const secretKey = "your_secret_key"; // Replace with your actual secret key    const expiration = 60; // Expiration time in seconds (how long an HMAC token should be valid for)
    const encoder = new TextEncoder();
    // Import the secret key for HMAC-SHA256 signing    const key = await crypto.subtle.importKey(      "raw",      encoder.encode(secretKey),      { name: "HMAC", hash: "SHA-256" },      false,      ["sign", "verify"],    );
    const url = new URL(request.url);
    // Check if the request URL starts with /generate/    if (url.pathname.startsWith("/generate/")) {      // Replace /generate/ with /      url.pathname = url.pathname.replace("/generate/", "/");
      const currentTimestamp = Math.floor(Date.now() / 1000); // Current timestamp in seconds
      // Data to authenticate: combine pathname and timestamp      const dataToAuthenticate = `${url.pathname}${currentTimestamp}`;
      // Sign the data with HMAC-SHA256      const signature = await crypto.subtle.sign(        "HMAC",        key,        encoder.encode(dataToAuthenticate),      );
      // Encode the timestamp and HMAC in a secure manner      const signatureBase64 = btoa(        String.fromCharCode(...new Uint8Array(signature)),      );      const signedData = `${currentTimestamp}-${signatureBase64}`;      const encodedSignedData = encodeURIComponent(signedData);
      // Create the signed URL      const signedURL = `${url}?verify=${encodedSignedData}`;
      // Return the signed URL in the response body      return new Response(signedURL, { status: 200 });    }
    // For all other request URLs, verify the signed URL    const params = new URLSearchParams(url.search);    const verifyParam = params.get("verify");
    if (!verifyParam) {      return new Response("Verification parameter is missing", { status: 403 });    }
    // Decode and split the verify parameter into timestamp and HMAC    const decodedVerifyParam = decodeURIComponent(verifyParam);    const [timestampStr, receivedMac] = decodedVerifyParam.split("-");
    // Parse timestamp and ensure it's a valid number    const timestamp = parseInt(timestampStr, 10);    if (isNaN(timestamp)) {      return new Response("Invalid timestamp", { status: 403 });    }
    // Check if the request has expired    const currentTimestamp = Math.floor(Date.now() / 1000);    if (currentTimestamp > timestamp + expiration) {      return new Response("Signed URL has expired", { status: 403 });    }
    // Remove the verify parameter to verify the URL    params.delete("verify");    url.search = params.toString();
    // Construct the data to authenticate for verification    const dataToVerify = `${url.pathname}${timestamp}`;
    // Verify the signature with HMAC-SHA256    const isValid = await crypto.subtle.verify(      "HMAC",      key,      new Uint8Array([...atob(receivedMac)].map((char) => char.charCodeAt(0))),      encoder.encode(dataToVerify),    );
    if (!isValid) {      return new Response("Invalid signature", { status: 403 });    }
    // Continue processing the request if the signature is valid    return fetch(request);  },};Was this helpful?
- Resources
- API
- New to Cloudflare?
- Products
- Sponsorships
- Open Source
- Support
- Help Center
- System Status
- Compliance
- GDPR
- Company
- cloudflare.com
- Our team
- Careers
- 2025 Cloudflare, Inc.
- Privacy Policy
- Terms of Use
- Report Security Issues
- Trademark