yum upgrades for production use, this is the repository for you.
Active subscription is required.
NGINX JWT authentication enables you to validate JSON Web Tokens directly at the reverse proxy layer. This approach offers significant advantages over application-level token validation. The nginx-module-jwt provides native NGINX JWT authentication capabilities, allowing you to offload security to the edge.
Modern APIs require robust authentication. JSON Web Tokens have become the standard for stateless authentication. While many developers implement JWT validation in application code, NGINX JWT authentication offers a more efficient approach. By validating tokens before requests reach your backend, you improve performance and simplify your security architecture.
Why Use NGINX JWT Authentication?
Traditional JWT validation happens in your application code. Every request carrying a token must be parsed, decoded, and verified by your backend. This approach has several drawbacks:
- Wasted resources: Invalid or expired tokens still consume backend processing power
- Code duplication: Every microservice needs JWT validation logic
- Inconsistent security: Different services may implement validation differently
- Increased latency: Backend processes must handle authentication before business logic
NGINX JWT authentication solves these problems elegantly:
- Early rejection: Invalid tokens are blocked before reaching your application
- Centralized security: One configuration secures all backend services
- Reduced complexity: Your application code focuses on business logic
- Better performance: NGINX’s efficient C implementation handles validation faster than interpreted languages
How the Module Works
The nginx-module-jwt operates in NGINX’s access phase. It validates tokens before content handlers execute. The module performs these steps:
- Extracts the JWT from the
Authorizationheader or a custom source like a cookie - Decodes the token and verifies its signature against your configured key
- Checks the
expclaim to reject expired tokens - Optionally validates the algorithm matches your expectations
- Makes JWT claims available as NGINX variables for downstream use
When validation fails, NGINX returns a 401 Unauthorized response immediately. When it succeeds, your backend receives the request with JWT claims available as headers or variables.
Installation on Rocky Linux and RHEL
The nginx-module-jwt is available from the GetPageSpeed repository. This repository supports RHEL-based distributions including Rocky Linux, AlmaLinux, CentOS Stream, and Fedora.
First, enable the GetPageSpeed repository:
sudo dnf install https://extras.getpagespeed.com/release-latest.rpm
Then install the module:
sudo dnf install nginx-module-jwt
After installation, load the module in your /etc/nginx/nginx.conf. Add this line before the events block:
load_module modules/ngx_http_auth_jwt_module.so;
Verify the module loads correctly:
nginx -t
You should see a successful configuration test message.
Configuration Directives
The module provides four directives for configuring NGINX JWT authentication.
auth_jwt
Syntax: auth_jwt $variable | on | off;
Default: off
Context: http, server, location
This directive enables or disables JWT validation. When set to on, the module extracts the token from the Authorization header. It expects a Bearer token format.
You can specify a variable to extract the token from a different source:
# Validate JWT from Authorization header
auth_jwt on;
# Validate JWT from a cookie named "auth_token"
auth_jwt $cookie_auth_token;
# Disable JWT validation
auth_jwt off;
auth_jwt_key
Syntax: auth_jwt_key value [encoding];
Default: none (required when auth_jwt is enabled)
Context: http, server, location
This directive specifies the key for verifying JWT signatures. The encoding parameter determines how the key value is interpreted:
utf8(default): Key is a plain UTF-8 stringhex: Key is a hexadecimal-encoded stringbase64: Key is Base64-encodedfile: Value is a path to a PEM-encoded key file
Examples of different key encodings:
# UTF-8 string key (default encoding)
auth_jwt_key "your-secret-key-here";
# Hexadecimal key
auth_jwt_key "0123456789abcdef0123456789abcdef" hex;
# Base64-encoded key
auth_jwt_key "eW91ci1zZWNyZXQta2V5" base64;
# RSA public key from PEM file
auth_jwt_key /etc/nginx/jwt-keys/public.pem file;
auth_jwt_alg
Syntax: auth_jwt_alg any | HS256 | HS384 | HS512 | RS256 | RS384 | RS512 | ES256 | ES384 | ES512;
Default: any
Context: http, server, location
This directive restricts which signing algorithms are accepted. The default any accepts all algorithms. However, for security you should specify your expected algorithm explicitly.
Security warning: Always specify the expected algorithm in production. Accepting any algorithm could allow attackers to forge tokens.
# Accept only HS256 tokens
auth_jwt_alg HS256;
# Accept only RS256 tokens (RSA)
auth_jwt_alg RS256;
auth_jwt_require
Syntax: auth_jwt_require $value ... [error=401 | 403];
Default: none
Context: http, server, location
This directive specifies additional validation requirements. It checks that all specified variables are non-empty and not equal to “0”. If any check fails, NGINX returns the specified error code.
Use this directive for role-based access control at the NGINX level:
map $jwt_claim_role $jwt_has_admin_role {
"\"admin\"" 1;
default 0;
}
server {
location /admin/ {
auth_jwt on;
auth_jwt_require $jwt_has_admin_role error=403;
}
}
Important: JWT claim values are JSON-encoded. Strings include surrounding quotes. Match against "\"admin\"" with escaped quotes.
Embedded Variables
The module exposes JWT data through NGINX variables. You can pass claims to backends or use them in logging.
$jwt_header_name
Returns the value of a specific JWT header field:
add_header X-JWT-Algorithm $jwt_header_alg;
$jwt_claim_name
Returns the value of a specific JWT claim:
add_header X-JWT-Subject $jwt_claim_sub;
add_header X-User-Role $jwt_claim_role;
$jwt_headers and $jwt_payload
These variables return the complete JWT headers and payload sections as JSON.
Note: All variable values are JSON-encoded. String values include surrounding double quotes.
Basic Configuration Example
This configuration protects API endpoints while leaving public routes accessible:
load_module modules/ngx_http_auth_jwt_module.so;
events {
worker_connections 1024;
}
http {
server {
listen 80;
server_name api.example.com;
auth_jwt_key "your-256-bit-secret-key-here";
auth_jwt_alg HS256;
auth_jwt off;
location /api/ {
auth_jwt on;
proxy_pass http://backend;
}
location /health {
return 200 "OK\n";
}
location /public/ {
proxy_pass http://backend;
}
}
}
Cookie-Based Authentication for SPAs
Single-page applications often store tokens in HTTP-only cookies. Configure NGINX JWT authentication to read from cookies:
server {
listen 443 ssl;
server_name app.example.com;
auth_jwt_key /etc/nginx/jwt-keys/public.pem file;
auth_jwt_alg RS256;
auth_jwt off;
location /dashboard/ {
auth_jwt $cookie_session_token;
proxy_pass http://app_backend;
}
location /login {
proxy_pass http://app_backend;
}
}
API Gateway Pattern with Claim Forwarding
A common pattern validates the JWT at the gateway. Then it forwards decoded claims to backend services as headers. Backends can trust these headers without re-validating tokens:
load_module modules/ngx_http_auth_jwt_module.so;
events {
worker_connections 1024;
}
http {
upstream user_service {
server 127.0.0.1:8001;
}
upstream order_service {
server 127.0.0.1:8002;
}
server {
listen 80;
server_name gateway.example.com;
auth_jwt_key "your-256-bit-secret-key-here";
auth_jwt_alg HS256;
location /users/ {
auth_jwt on;
proxy_set_header X-User-Id $jwt_claim_sub;
proxy_set_header X-User-Role $jwt_claim_role;
proxy_set_header Authorization "";
proxy_pass http://user_service/;
}
location /orders/ {
auth_jwt on;
proxy_set_header X-User-Id $jwt_claim_sub;
proxy_set_header X-User-Role $jwt_claim_role;
proxy_set_header Authorization "";
proxy_pass http://order_service/;
}
}
}
Role-Based Access Control Example
Implement fine-grained access control based on JWT claims:
load_module modules/ngx_http_auth_jwt_module.so;
events {
worker_connections 1024;
}
http {
map $jwt_claim_role $is_admin {
"\"admin\"" 1;
"\"superuser\"" 1;
default 0;
}
map $jwt_claim_role $is_editor {
"\"admin\"" 1;
"\"editor\"" 1;
default 0;
}
server {
listen 80;
server_name cms.example.com;
auth_jwt_key "your-256-bit-secret-key-here";
auth_jwt_alg HS256;
location /content/ {
auth_jwt off;
proxy_pass http://cms_backend;
}
location /edit/ {
auth_jwt on;
auth_jwt_require $is_editor error=403;
proxy_pass http://cms_backend;
}
location /admin/ {
auth_jwt on;
auth_jwt_require $is_admin error=403;
proxy_pass http://cms_backend;
}
}
}
Redirect to Login on Authentication Failure
For web applications, redirect unauthenticated users to a login page:
server {
listen 80;
server_name app.example.com;
auth_jwt_key "your-secret-key";
auth_jwt_alg HS256;
auth_jwt off;
location @login_redirect {
return 302 $scheme://$host/login?redirect=$request_uri;
}
location /app/ {
auth_jwt $cookie_auth_token;
error_page 401 = @login_redirect;
proxy_pass http://app_backend;
}
location /login {
proxy_pass http://app_backend;
}
}
Using RSA Keys for Production
RSA keys provide better security than shared secrets. The private key stays on your authentication server. NGINX only needs the public key to verify signatures.
Generate an RSA key pair:
openssl genrsa -out private.pem 2048
openssl rsa -in private.pem -pubout -out public.pem
Configure NGINX to use the public key:
auth_jwt_key /etc/nginx/jwt-keys/public.pem file;
auth_jwt_alg RS256;
Benefits of asymmetric keys include:
- Only public keys need distribution to NGINX servers
- Compromised NGINX servers cannot forge tokens
- Key rotation is simpler
Testing Your NGINX JWT Authentication
Generate test tokens using Python:
import hmac
import hashlib
import base64
import json
import time
def base64url_encode(data):
return base64.urlsafe_b64encode(data).rstrip(b"=").decode("ascii")
key = b"your-256-bit-secret-key-here"
header = {"alg": "HS256", "typ": "JWT"}
header_b64 = base64url_encode(json.dumps(header, separators=(",", ":")).encode())
payload = {
"sub": "user123",
"name": "Test User",
"role": "admin",
"exp": int(time.time()) + 3600
}
payload_b64 = base64url_encode(json.dumps(payload, separators=(",", ":")).encode())
message = f"{header_b64}.{payload_b64}"
signature = hmac.new(key, message.encode(), hashlib.sha256).digest()
signature_b64 = base64url_encode(signature)
token = f"{message}.{signature_b64}"
print(token)
Test with curl:
TOKEN="your.jwt.token"
curl -H "Authorization: Bearer $TOKEN" http://localhost/api/endpoint
Test that invalid tokens are rejected:
curl -w "%{http_code}\n" http://localhost/api/endpoint
Performance Considerations
JWT validation in NGINX is highly efficient. The module caches configured keys in memory. There is no filesystem overhead per request.
For high-traffic scenarios, consider these optimizations:
- Implement token caching in your application layer
- Use shorter token expiration times
- Place rate limiting before JWT validation
HMAC algorithms are faster than RSA or ECDSA. However, the security benefits of asymmetric cryptography often outweigh the minor performance difference.
Security Best Practices
Specify the Algorithm Explicitly
Never use auth_jwt_alg any in production. This setting could allow algorithm confusion attacks:
# Good
auth_jwt_alg RS256;
# Bad
auth_jwt_alg any;
Use HTTPS
Always use HTTPS when transmitting JWTs. While JWTs are signed, they are not encrypted. Anyone intercepting the token can read its contents.
For proper HTTPS configuration, see our guide on NGINX TLS 1.3 hardening.
Set Token Expiration
The module automatically validates the exp claim. Use reasonable expiration times:
- Access tokens: 15 minutes to 1 hour
- Refresh tokens: Days to weeks
Protect Your Keys
For HMAC keys:
- Use cryptographically random keys of at least 256 bits
- Never commit keys to version control
- Rotate keys periodically
For RSA keys, set appropriate file permissions:
chmod 600 /etc/nginx/jwt-keys/public.pem
chown root:root /etc/nginx/jwt-keys/public.pem
Troubleshooting NGINX JWT Authentication
401 Errors on Valid Tokens
Check these common causes:
- Key encoding mismatch: Ensure the key encoding matches token generation
- Algorithm mismatch: Verify tokens use the algorithm specified in
auth_jwt_alg - Token expiration: Check the token’s
expclaim is in the future
Enable debug logging for detailed errors:
error_log /var/log/nginx/error.log debug;
Common log messages include:
JWT: failed to find a jwt– Token not in expected locationJWT: failed to parse jwt– Invalid format or signatureJWT: invalid algorithm– Algorithm doesn’t match configurationJWT: the jwt has expired– Token’sexpis in the past
Empty JWT Variables
If variables like $jwt_claim_sub are empty:
- Ensure
auth_jwtis enabled (notoff) - Check claim names are correct and case-sensitive
- Remember values are JSON-encoded with quotes
Conclusion
NGINX JWT authentication with nginx-module-jwt provides a powerful way to secure your APIs. By validating tokens at the edge, you reduce backend load and centralize security policy. This approach simplifies your application architecture significantly.
Key takeaways for implementing NGINX JWT authentication:
- Install from the GetPageSpeed repository for easy setup
- Use RSA keys in production for better security
- Always specify the expected algorithm
- Leverage JWT variables to forward claims to backends
- Implement role-based access control with
auth_jwt_require
For more information, visit the nginx-module-jwt GitHub repository.
