---
title: Validate JSON web tokens (JWT)
description: Extract a JWT from the Authorization header, verify its HMAC-SHA256 signature using the WebCrypto API, and validate its claims.
image: https://edgetunnel-b2h.pages.dev/core-services-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://edgetunnel-b2h.pages.dev/rules/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# Validate JSON web tokens (JWT)

Extract a JWT from the Authorization header, verify its HMAC-SHA256 signature using the WebCrypto API, and validate its claims.

Caution when using in production

The example code contains a placeholder HMAC secret of `"mysecretsymmetrickey"`. To best protect your resources, change the secret to a strong, unique value in the Snippets editor before saving your code.

**JavaScript**

```js
export default {
  async fetch(request) {
    const HMAC_SECRET = "mysecretsymmetrickey"; // Change this to your secret key
    const CLOCK_TOLERANCE = 30; // Tolerance for clock skew in seconds


    // Extract JWT token from "Authorization: Bearer" header
    function getJWTToken(request) {
      const authorizationHeader = request.headers.get("Authorization");
      if (authorizationHeader && authorizationHeader.startsWith("Bearer ")) {
        return authorizationHeader.substring(7, authorizationHeader.length);
      }
      return null;
    }


    // Convert a base64url string to a Uint8Array
    function base64urlToUint8Array(base64url) {
      const base64 = base64url
        .replace(/-/g, "+")
        .replace(/_/g, "/")
        .padEnd(base64url.length + ((4 - (base64url.length % 4)) % 4), "=");
      const binary = atob(base64);
      return Uint8Array.from(binary, (c) => c.charCodeAt(0));
    }


    // Decode a base64url-encoded JWT segment to a JSON object
    function decodeSegment(s) {
      const bytes = base64urlToUint8Array(s);
      return JSON.parse(new TextDecoder().decode(bytes));
    }


    // Validate JWT token structure, cryptographic signature, and claims.
    async function validateJWT(token) {
      const parts = token.split(".");
      if (parts.length !== 3) {
        throw new Error("Invalid JWT format");
      }


      const [headerB64, payloadB64, signatureB64] = parts;
      const decodedHeader = decodeSegment(headerB64);
      const decodedPayload = decodeSegment(payloadB64);


      // Verify the algorithm.
      // This prevents algorithm-confusion attacks where an attacker changes the "alg" header to bypass signature verification.
      // Never branch on the token's alg header to decide how to verify.
      // Always enforce the algorithm you expect.
      if (decodedHeader.alg !== "HS256") {
        throw new Error("Unsupported algorithm");
      }


      // Verify the cryptographic signature.
      // Import the shared secret as an HMAC-SHA256 key, then verify that the signature matches "header.payload".
      // If this fails, the token was tampered with or signed with a different key.
      const signature = base64urlToUint8Array(signatureB64);
      const data = new TextEncoder().encode(`${headerB64}.${payloadB64}`);
      const key = await crypto.subtle.importKey(
        "raw",
        new TextEncoder().encode(HMAC_SECRET),
        { name: "HMAC", hash: "SHA-256" },
        false,
        ["verify"],
      );
      const valid = await crypto.subtle.verify(
        { name: "HMAC" },
        key,
        signature,
        data,
      );


      if (!valid) {
        throw new Error("Invalid signature");
      }


      // Validate standard claims.
      // The claims a JWT must contain depends on your use case.
      const now = Math.floor(Date.now() / 1000);


      // exp (expiration): Reject tokens that have expired.
      // A small clock tolerance accounts for clock skew between servers.
      if (typeof decodedPayload.exp !== "number") {
        throw new Error("Missing exp claim");
      }
      if (decodedPayload.exp < now - CLOCK_TOLERANCE) {
        throw new Error("JWT has expired");
      }


      // iat (issued at): Ensure the token declares when it was created.
      if (typeof decodedPayload.iat !== "number") {
        throw new Error("Missing iat claim");
      }


      // Additional claims per RFC 7519 (https://www.rfc-editor.org/rfc/rfc7519.html):
      // - nbf (not before): Reject tokens used before their valid start time.
      //     if (decodedPayload.nbf > now + CLOCK_TOLERANCE) throw new Error("JWT is not yet valid");
      // - iss (issuer): Ensure the token was issued by a trusted authority.
      //     if (decodedPayload.iss !== "https://auth.example.com") throw new Error("Invalid issuer");
      // - aud (audience): Ensure the token is intended for your service.
      //     if (decodedPayload.aud !== "my-api") throw new Error("Invalid audience");
      // - sub (subject): Identify the principal the token refers to.
      // - jti (JWT ID): Unique token identifier, useful for preventing replay attacks.


      return true;
    }


    // Execute the function to extract JWT token
    const jwtToken = getJWTToken(request);


    // If the token is not provided, serve 401 Forbidden
    if (!jwtToken) {
      return new Response("Missing JWT token", { status: 401 });
    }


    try {
      // If the token is valid, the validateJWT function will not error and serve the actual response
      // An example of a valid token that will expire in 2033 is "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNjI0OTkyMDAwLCJleHAiOjIwMDExMjAwMDB9._qgQ_TMrGfYgOoA8HtTZwEGoj8zAPWxsz8CT1jEAGzo"
      await validateJWT(jwtToken);
      return fetch(request);
    } catch {
      return new Response("Invalid JWT token", { status: 401 });
    }
  },
};
```

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://edgetunnel-b2h.pages.dev/rules/snippets/examples/jwt-validation/#page","headline":"Validate JSON web tokens (JWT) · Cloudflare Rules docs","description":"Extract a JWT from the Authorization header, verify its HMAC-SHA256 signature using the WebCrypto API, and validate its claims.","url":"https://edgetunnel-b2h.pages.dev/rules/snippets/examples/jwt-validation/","inLanguage":"en","image":"https://edgetunnel-b2h.pages.dev/core-services-preview.png","dateModified":"2026-04-20","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://edgetunnel-b2h.pages.dev/#website","name":"Cloudflare Docs","url":"https://edgetunnel-b2h.pages.dev/"},"keywords":["Authentication","Request modification"]}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/rules/","name":"Rules"}},{"@type":"ListItem","position":3,"item":{"@id":"/rules/snippets/","name":"Cloudflare Snippets"}},{"@type":"ListItem","position":4,"item":{"@id":"/rules/snippets/examples/","name":"Snippets examples"}},{"@type":"ListItem","position":5,"item":{"@id":"/rules/snippets/examples/jwt-validation/","name":"Validate JSON web tokens (JWT)"}}]}
```
