API auth signature mismatch? Start here
Your API call failed because the signature doesn't match what the server computed. Here's how to fix it, from quick checks to deep debugging.
The 30-second fix: Check your timestamp and nonce
What's actually happening here is the server rejects your request because the signature doesn't match the one it computed using the same inputs. The most common reason? Your timestamp is too far off from the server's clock — usually more than 5 minutes. Open your request payload and check the timestamp value. If it's more than 300 seconds old, the server will silently regenerate a different signature and flag yours as invalid.
Run date +%s on Linux or Get-Date -UFormat %s in PowerShell. Compare that to the timestamp field in your request. If they differ by more than 300 seconds, fix it. Some APIs also require the nonce to be unique per request. If you're reusing a nonce (e.g., hardcoded in a test script), you'll get a signature mismatch every time. Generate a random UUID for each call.
The 5-minute fix: Verify the signing string construction
If the timestamp and nonce look fine, the issue is almost certainly in how you build the string that gets signed. Different APIs expect different orders. The classic AWS Signature V4, for example, requires this exact order:
HTTPMethod + "\n" +
CanonicalURI + "\n" +
CanonicalQueryString + "\n" +
CanonicalHeaders + "\n" +
SignedHeaders + "\n" +
PayloadHash
But many custom APIs use something simpler like:
HTTPMethod + URI + Timestamp + Nonce + RequestBody
Here's where people trip: the request body must be exactly as sent. If you're sending JSON, the body string must be the same bytes the server sees. Any whitespace difference — extra spaces in pretty-printed JSON versus minified — will change the hash and cause a mismatch. Log the exact body string your code generates and compare it to what the server expects. Use a tool like curl --trace-ascii to see exactly what bytes go over the wire.
Also check if the signature algorithm is HMAC-SHA256, HMAC-SHA1, or plain SHA256. They're not interchangeable. The server might expect a specific algorithm that you're not using.
The 15+ minute fix: Rebuild the signature step by step
If the above didn't work, you need to replicate the server's signing logic exactly. Here's the systematic approach:
- Capture a working request — if you have any client that works (official SDK, Postman, etc.), capture its raw request. Use Wireshark or
tcpdumpto grab the exact headers and body. Compare them to your request byte-for-byte. - Extract the signing key — some APIs derive a signing key from your secret key using HMAC and the date. For example, AWS does:
HMAC(HMAC(HMAC("AWS4" + SecretKey, Date), Region), Service), "aws4_request". If you skip the date derivation, you'll get a different key, and the signature will never match. - Verify the canonical request — list all headers the server includes in the signed headers set. Some APIs require
Host,Content-Type, andX-API-Key. Missing one header from the signing string will break the signature. Check theSignedHeadersheader in your request — it should list the exact headers you included in the canonical headers block. - Test with a minimal payload — strip the request body to empty or a known simple string. If the signature matches with an empty body but not with your full payload, the issue is in how you compute the payload hash. Ensure you hash the raw bytes, not a stringified version of the JSON.
- Check encoding — signature strings are often URL-encoded or base64-encoded before comparison. If the server expects base64 but you're sending hex, it won't match. If the server expects lowercase hex but you're sending uppercase, it won't match. Log the exact signature your code produces and the one the server expects (from the response body or headers).
The real fix is often something stupid like an extra newline at the end of the signing string. I've seen a trailing space in the HTTP method string cause a 403 for three hours. Use openssl dgst -sha256 -hmac "$key" to manually compute the signature and compare it to what your code produces. If they differ, your code has a bug.
One last thing: some APIs require the Authorization header to include the algorithm, credential scope, and signed headers in a specific order. For example:
Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/iam/aws4_request, SignedHeaders=content-type;host;x-amz-date, Signature=...
If you're missing a semicolon or a header name, the server will parse it incorrectly and compute a different signature. Check the exact format in the API docs — they're rarely flexible.
Pro tip: Use a library like
crypto-jsfor JavaScript,hmacfor Python, orSystem.Security.Cryptographyfor C#. Don't write your own HMAC implementation unless you enjoy debugging encoding issues for hours.
Was this solution helpful?