This document describes common issues and solutions for nginx-auth-jwt.
Symptom: All requests return 401 even with a valid JWT.
Cause: The JWT is not being sent in the correct format, or auth_jwt is not configured for the matching location.
Solution:
Verify the Authorization header format. The default method uses Bearer Token:
curl -H 'Authorization: Bearer <your-jwt-token>' https://example.com/protectedIf the token is passed via a cookie or query parameter, configure the token parameter:
# Cookie
auth_jwt "realm" token=$cookie_auth_token;
# Query parameter
auth_jwt "realm" token=$arg_token;Check that auth_jwt is not accidentally set to off in a child location that overrides a parent configuration:
server {
auth_jwt "realm";
auth_jwt_key_file /etc/nginx/keys/jwks.json;
location /public {
auth_jwt off; # Explicitly disabled here
}
}Symptom: nginx fails to start or logs show key file errors.
Cause: The path specified in auth_jwt_key_file does not exist or is not readable by the nginx worker process.
Solution:
Verify the file exists and check permissions:
ls -la /etc/nginx/keys/jwks.jsonEnsure the nginx worker process can read the file:
chmod 640 /etc/nginx/keys/jwks.json
chown root:nginx /etc/nginx/keys/jwks.jsonValidate the nginx configuration:
nginx -tSymptom: Requests return 401 with signature-related errors in the logs.
Cause: The key format does not match the JWT algorithm, or the key file is malformed.
Solution:
Check the key format. JWKS (default) and keyval formats have different structures:
// JWKS format (jwks)
{
"keys": [
{
"kty": "RSA",
"kid": "my-key-id",
"use": "sig",
"n": "...",
"e": "AQAB"
}
]
}// keyval format (keyval)
{
"my-key-id": "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----"
}Specify the correct format explicitly in the directive:
auth_jwt_key_file /etc/nginx/keys/jwks.json; # JWKS (default)
auth_jwt_key_file /etc/nginx/keys/keys.json keyval; # keyvalHMAC algorithms cannot be used with the keyval format (values are restricted to PEM public keys). Use a JWKS kty: "oct" entry instead:
{
"keys": [
{
"kty": "oct",
"kid": "my-hmac-key",
"alg": "HS256",
"k": "<base64url-encoded secret>"
}
]
}Ensure the kid in the JWT header matches a key in the key file. Enable debug logging to inspect key lookup behavior (see Log Inspection).
Symptom: Requests return 401 (or 403 if explicitly configured) even though the claim value appears correct.
Cause: Type mismatch between the expected value and the actual claim type in the JWT.
Solution:
Use the json= prefix for non-string types:
# Wrong: compares integer claim as string "1697461112"
auth_jwt_require_claim iat eq 1697461112;
# Correct: compares as integer
auth_jwt_require_claim iat eq json=1697461112;# Wrong: compares boolean claim as string "true"
auth_jwt_require_claim active eq true;
# Correct: compares as boolean
auth_jwt_require_claim active eq json=true;For array operations (intersect, nintersect, in, nin), use JSON array syntax:
# Wrong: single string, not an array
auth_jwt_require_claim roles intersect admin;
# Correct: JSON array
auth_jwt_require_claim roles intersect json=["admin","editor"];To inspect the actual claim values in the JWT, decode the token at jwt.io or use:
echo '<payload-part>' | base64 -d | python3 -m json.toolSymptom: Requests fail when using auth_jwt_key_request, or key fetching is slow.
Cause: The subrequest endpoint is returning errors, is not internal, or responses are compressed.
Solution:
Mark the key fetch location as internal and configure caching:
proxy_cache_path /data/nginx/cache levels=1 keys_zone=jwks_cache:10m;
server {
location / {
auth_jwt "protected";
auth_jwt_key_request /jwks_uri;
}
location = /jwks_uri {
internal;
proxy_cache jwks_cache;
proxy_cache_valid 200 1h;
proxy_pass https://idp.example.com/.well-known/jwks.json;
# Disable compression to avoid Content-Encoding errors
proxy_set_header Accept-Encoding "";
}
}If the upstream returns a compressed response, the module will reject it. Disable compression by setting Accept-Encoding: "" in the proxy request.
Note: auth_jwt_key_request cannot be used inside a subrequest. If the module is processing a subrequest context, use auth_jwt_key_file or set auth_jwt_phase preaccess instead.
Symptom: auth_jwt_require_claim with dot-notation fails even though the claim exists in the JWT payload.
Cause: auth_jwt_allow_nested is not configured.
Solution:
Add auth_jwt_allow_nested to the location or server block:
location /api/ {
auth_jwt_allow_nested;
auth_jwt_require_claim user.role eq admin;
}To access a claim name that literally contains the delimiter character, use the quote parameter:
auth_jwt_allow_nested delimiter=. quote=';
# Access the claim literally named "user.role"
auth_jwt_require_claim 'user.role' eq admin;Note: auth_jwt_allow_nested must be in the same or parent context as the auth_jwt_require_claim directive.
Enable debug logging to diagnose JWT validation issues:
error_log /var/log/nginx/error.log debug;Search for auth_jwt-related log entries:
grep 'auth_jwt' /var/log/nginx/error.logCommon log patterns to look for:
auth_jwt: token was not provided— JWT not found in requestauth_jwt: failed to parse jwt token— JWT structure is malformedauth_jwt: rejected due to missing algorithm— JOSE headeralgmissingauth_jwt: rejected due to missing signature key or signature validate failure— key mismatch or algorithm mismatchauth_jwt: rejected due to %V variable invalid—auth_jwt_requirecheck failedauth_jwt: rejected due to token expired—expclaim is in the past
Always validate the nginx configuration before reloading:
nginx -tCommon configuration errors:
auth_jwt_key_filepath not found: Check file path and permissionsauth_jwt_require_claiminvalid operator: Use one ofeq ne gt ge lt le intersect nintersect in ninauth_jwt_require_claimvalue too large: expected values are limited to 4 KiB (4096 bytes)
- README.md: Module overview and quick start
- DIRECTIVES.md: Directive and variable reference
- EXAMPLES.md: Configuration examples
- INSTALL.md: Installation guide
- SECURITY.md: Security considerations
- TROUBLESHOOTING.md: Troubleshooting
- COMMERCIAL_COMPATIBILITY.md: Commercial version compatibility