How to Fix "JWT signature verification failed" in API Requests
Quick answer
When building or consuming REST APIs authenticated with JSON Web Tokens (JWT), encountering a signature verification failure is one of the most common security...
When building or consuming REST APIs authenticated with JSON Web Tokens (JWT), encountering a signature verification failure is one of the most common security errors. This error occurs when the receiving server attempts to validate the cryptographic signature in the third segment of the token against the header and payload, but the calculated hash does not match the provided signature. Understanding how signature generation works across RS256 and HS256 algorithms allows developers to quickly identify and fix key mismatches, payload mutations, or header encoding discrepancies.
The Problem
When sending requests or configuring components in your development environment, the application or HTTP client fails with the following exact error trace:
# Example 1: Node.js / jsonwebtoken error
JsonWebTokenError: invalid signature
at /app/node_modules/jsonwebtoken/verify.js:133:19
at GMT.verify (/app/node_modules/jsonwebtoken/verify.js:101:12)
# Example 2: Python / PyJWT error
jwt.exceptions.InvalidSignatureError: Signature verification failed
# Example 3: Go / golang-jwt error
jwt: signature is invalid
This error halts script execution, prevents API communication, or results in immediate client-side connection drops.
Why It Happens
This failure occurs due to misconfigurations at the networking, cryptographic, or application protocol layer. The primary causes include:
- Secret Key or Public Key Mismatch: The signing server used a different secret key or private key than the verification server configured in its environment variables.
- Symmetric vs. Asymmetric Algorithm Confusion: The token was issued using an asymmetric algorithm like
RS256(RSA private/public key pair) but the verification endpoint evaluated it using a symmetric algorithm likeHS256(HMAC shared secret). - Payload Mutation in Transit: Intermediate proxies, API gateways, or client-side transformers modified body headers, whitespace, or claim encoding, invalidating the hash calculated over
base64url(header) + "." + base64url(payload). - Base64URL Encoding/Padding Mismatch: Standard Base64 padding characters (
=) or unescaped URL symbols (+,/) were retained during token generation or decoding rather than applying Base64URL encoding rules.
The Fix
Follow these step-by-step solutions to resolve the error in your environment.
Step 1: Verify the Algorithm and Matching Public Key
Ensure that the header of your token specifies the exact algorithm expected by your decoder library. For asymmetric signing (RS256), decode the unverified token using command-line tools or online decoders to check the header:
# Base64URL decode the JWT header
echo "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9" | openssl base64 -d -A
If the header contains {"alg": "RS256"}, ensure your verification logic passes the public key certificate in PEM format rather than a raw string passphrase:
import jwt
from pathlib import Path
public_key = Path("public_key.pem").read_text()
try:
# Always explicitly restrict allowed algorithms to prevent algorithm confusion attacks
payload = jwt.decode(token, public_key, algorithms=["RS256"])
except jwt.exceptions.InvalidSignatureError:
print("JWT signature verification failed: Ensure public key matches the issuing private key.")
Step 2: Check Secret Key Encoding in Symmetric HS256
When using HS256, signature verification fails if one system parses the secret key as plain ASCII/UTF-8 while the other decodes it as Base64 or Hex binary. Align your verification logic in Node.js or Python to handle raw byte arrays explicitly:
const jwt = require('jsonwebtoken');
// Ensure secret is converted to a Buffer if encoded as hex or base64
const secretKey = Buffer.from(process.env.JWT_SECRET, 'utf8');
try {
const decoded = jwt.verify(token, secretKey, { algorithms: ['HS256'] });
console.log('Verified payload:', decoded);
} catch (err) {
if (err.name === 'JsonWebTokenError') {
console.error('JWT signature verification failed:', err.message);
}
}
Step 3: Audit Environment Variables in Multi-Service Deployments
In microservice architectures, verify that the auth microservice issuing the token and the downstream API service consuming the token draw from the same configuration source. Run the following command in your container environment to check for trailing newlines or quotation marks in the secret variable:
# Output secret key length and representation without exposing plain text
python3 -c "import os; s = os.getenv('JWT_SECRET', ''); print(f'Length: {len(s)}, HasNewline: {s.endswith("\n")}')"
Still Not Working?
Reverse Proxy Header Strip or Truncation
If signature verification still fails selectively in production, inspect reverse proxies (like Nginx, AWS ALB, or Cloudflare). Certain API proxies limit HTTP header size or strip double quotes and characters from the Authorization: Bearer <token> header. Inspect the incoming raw header at your web server layer using cURL to confirm the complete token string is passed without truncation:
curl -v -H "Authorization: Bearer <YOUR_JWT_TOKEN>" https://api.yourdomain.com/v1/user
If the raw token length output by the server differs from the client payload, update your proxy's maximum header size limit (e.g., large_client_header_buffers 4 16k; in Nginx configuration).