---
title: digest.hmac_sha256_base64
summary: null
url: >-
  https://www.fastly.com/documentation/reference/vcl/functions/cryptographic/digest-hmac-sha256-base64
---

```
STRING digest.hmac_sha256_base64(STRING key, STRING s)
```

**Available in:** all subroutines

Returns the HMAC-SHA256 of `message` using `key`, encoded as a Base64 string.

## Parameters

| Parameter | Type   | Description                         |
| --------- | ------ | ----------------------------------- |
| `key`     | STRING | The secret key for HMAC computation |
| `message` | STRING | The message to authenticate         |

The `key` is used directly as the HMAC key. For keys longer than 64 bytes (the SHA-256 block size), the key is first hashed with SHA-256 before use, as specified in RFC 2104.

## Return value

Returns a 44-character Base64-encoded string representing the 256-bit (32-byte) HMAC, using standard Base64 encoding (RFC 4648 Section 4) with padding.

Example output: `CV1aIf5tBkbbIj/fPeZDa7jfsvqwtRZ37PZEH89fKmc=`

If `key` is empty or not set, the function returns an empty string (not set).

## Base64 encoding

This function uses standard Base64 encoding as defined in RFC 4648 Section 4:

| Property | Value             |
| -------- | ----------------- |
| Alphabet | `A-Za-z0-9+/`     |
| Padding  | `=` (always used) |

For URL-safe Base64 output, apply `digest.base64url_nopad()` to convert the result, or compute the hex version and convert with `bin.hex_to_base64url_nopad()`.

## Examples

### Basic usage

```vcl
declare local var.hmac STRING;
set var.hmac = digest.hmac_sha256_base64("secret-key", "hello world");
# Result: CV1aIf5tBkbbIj/fPeZDa7jfsvqwtRZ37PZEH89fKmc=
```

To verify this output using OpenSSL:

```term
$ echo -n "hello world" | openssl dgst -sha256 -hmac "secret-key" -binary | openssl base64
CV1aIf5tBkbbIj/fPeZDa7jfsvqwtRZ37PZEH89fKmc=
```

### Signing for an API that expects Base64 signatures

Many APIs expect Base64-encoded HMAC signatures:

```vcl
sub vcl_recv {
    declare local var.string_to_sign STRING;
    declare local var.signature STRING;
    declare local var.api_secret STRING;

    set var.api_secret = table.lookup(api_keys, "service-secret");
    if (var.api_secret == "") {
        error 500 "API secret not configured";
    }

    set var.string_to_sign = req.http.Date + "\n" + req.url.path;
    set var.signature = digest.hmac_sha256_base64(var.api_secret, var.string_to_sign);

    set req.http.Authorization = "Signature " + var.signature;
}
```

### Verifying a webhook with Base64 signature

```vcl
sub vcl_recv {
    declare local var.expected_sig STRING;
    declare local var.secret STRING;

    set var.secret = table.lookup(webhook_secrets, "stripe");
    if (var.secret == "") {
        error 500 "Webhook secret not configured";
    }

    set var.expected_sig = digest.hmac_sha256_base64(var.secret, req.body);

    # Use constant-time comparison to prevent timing attacks
    if (!digest.secure_is_equal(var.expected_sig, req.http.Stripe-Signature)) {
        error 401 "Invalid signature";
    }
}
```

### Converting to URL-safe Base64

If you need URL-safe Base64 without padding (common in JWTs and URLs):

```vcl
declare local var.hmac_b64 STRING;
declare local var.hmac_b64url STRING;

set var.hmac_b64 = digest.hmac_sha256_base64("secret-key", "hello world");
# Result: CV1aIf5tBkbbIj/fPeZDa7jfsvqwtRZ37PZEH89fKmc=

# Convert + to -, / to _, and remove padding
set var.hmac_b64url = regsuball(regsuball(var.hmac_b64, "\+", "-"), "/", "_");
set var.hmac_b64url = regsub(var.hmac_b64url, "=+$", "");
# Result: CV1aIf5tBkbbIj_fPeZDa7jfsvqwtRZ37PZEH89fKmc
```

## Comparison with hex output

| Function                      | Output                                                               | Length   |
| ----------------------------- | -------------------------------------------------------------------- | -------- |
| `digest.hmac_sha256()`        | `0x095d5a21fe6d0646db223fdf3de6436bb8dfb2fab0b51677ecf6441fcf5f2a67` | 66 chars |
| `digest.hmac_sha256_base64()` | `CV1aIf5tBkbbIj/fPeZDa7jfsvqwtRZ37PZEH89fKmc=`                       | 44 chars |

Base64 encoding is more compact (44 characters vs 66 for hex) and is often required by APIs and protocols.

## Security considerations

### Constant-time comparison

When comparing HMAC values for authentication, always use `digest.secure_is_equal()` to prevent timing attacks. String comparison with `==` leaks information about which bytes matched, potentially allowing an attacker to forge valid authentication tags:

```vcl
# CORRECT - constant-time comparison
if (digest.secure_is_equal(var.computed_hmac, req.http.X-Signature)) { ... }
```

### Validate keys before use

Always check that key lookups succeed:

```vcl
declare local var.key STRING;
set var.key = table.lookup(secrets, "api-key");
if (var.key == "") {
    error 500 "API key not found";
}
```

## Related content

- `digest.hmac_sha256()` - Returns hex-encoded output with `0x` prefix.
- `digest.hmac_sha512_base64()` - HMAC-SHA512 with Base64 output.
- `digest.hmac_sha1_base64()` - HMAC-SHA1 with Base64 output (legacy).
- `digest.secure_is_equal()` - Constant-time string comparison.
