Skip to content
apikey.tools
HMACSignatures

API Request Header Signature Validator

Reproduce a canonical string, compute its HMAC, and compare against the signature you received.

HMACRuns in this tabInput not transmitted

Input

The value your caller sent. Compared in constant time against the computed signature.

Reproduce the canonical string exactly, including newlines and ordering.

Output

computing

Signed requests are stronger than bearer tokens. The secret is never transmitted, so intercepting a request does not yield the credential, and the signature covers the request itself so nothing can be modified in flight.

The cost is that both sides must construct byte-identical canonical strings, and when they do not the error is always the same unhelpful "signature mismatch".

The canonical string is the whole problem

A canonical string is a deterministic serialisation of the parts of the request being authenticated. Something like:

POST
/v1/orders
1712345678
{"total":2000}

Every detail is load-bearing:

  • Line endings. \n or \r\n — they produce different signatures.
  • Trailing newline. Present or absent, and both sides must agree.
  • Case. POST or post, Content-Type or content-type.
  • Header ordering. Almost always lowercase and lexicographically sorted, but the spec must say so.
  • Path encoding. Whether /a b is signed encoded or decoded.
  • Query ordering. Sorted by key, and whether values are encoded before or after sorting.

Paste your reconstruction into the payload field, compute, and compare against what you received. If they differ, the canonical string is wrong somewhere above.

Debugging a mismatch

Work down this list; the cause is almost always in the first three:

  1. Raw body versus re-serialised. If your framework parsed the JSON, you are signing different bytes. Capture the raw body.
  2. Trailing newline. Editors and shell here-docs add one silently. Check the byte length.
  3. Wrong secret. Test versus live, or a value with whitespace from a copy-paste.
  4. Encoding. Hex versus Base64, uppercase versus lowercase hex.
  5. Timestamp. If the timestamp is part of the signed string, both sides must use the identical value — the one in the header, not a freshly generated one.

The "first difference" readout on the hash comparator is useful here: if two signatures diverge at character zero, it is a different secret or a different canonical string; if they match for most of their length, it is an encoding difference.

Constant-time comparison, always

Comparing signatures with == leaks the correct value through timing. Enough requests and an attacker recovers a valid signature byte by byte. This page compares in constant time; your server must too.

Reject old timestamps

A valid signature stays valid forever unless something bounds it. Include a timestamp in the signed string and reject anything outside a few minutes. Otherwise a captured request is replayable indefinitely — the signature does not expire on its own.

Frequently asked questions

Should I sign the whole request or just the body?

Sign everything that must not be modified: the method, the path, the timestamp, the body, and any header whose value matters. Signing only the body lets an attacker replay the same body against a different endpoint.

What if my API needs to support two secrets during rotation?

Compute against both and accept if either matches. That is the only way to rotate a signing secret without downtime — see the rotation planner for the full sequence.

Is a signature better than an API key in a header?

Yes, meaningfully. The secret never crosses the network, so a compromised TLS terminator or a logged request does not disclose it. The trade-off is a more complex client, which is why most public APIs still use bearer tokens.

How do I generate the signature side?

Use the HMAC signature generator with the same canonical string. Both tools use the same engine, so a value that verifies in one will verify in the other.

More signatures tools

Compute and verify request signatures