Skip to content

JWT Security Headers — Protecting Token-Based APIs with HTTP Security Headers

DodaTech Updated 2026-06-28 4 min read

In this tutorial, you will learn about JWT Security Headers. We cover key concepts, practical examples, and best practices to help you master this topic.

JWT security headers protect token transmission and storage by enforcing HTTPS, preventing MIME sniffing, controlling resource loading, and setting secure cookie attributes for token-bearing requests.

What You'll Learn

  • HTTP security headers for JWT-protected APIs
  • Secure cookie configuration for refresh tokens
  • CSP directives to prevent token exfiltration
  • HSTS preload and certificate transparency
  • Custom headers for token fingerprinting

Why It Matters

Security headers prevent entire classes of attacks. A missing Strict-Transport-Security header enables SSL stripping. A missing Content-Security-Policy allows token exfiltration via XSS. DodaTech's API gateway enforces 14 security headers, blocking token theft attempts at the network layer.

Real-World Use

A dashboard application uses JWTs for authentication. The API sets HSTS, CSP with connect-src restrictions, and X-Content-Type-Options: nosniff. When an XSS vulnerability was discovered, the CSP prevented attackers from exfiltrating tokens to external servers.

flowchart LR
    A["Browser"] --> B["API Gateway"]
    B --> C["Security Header Middleware"]
    C --> D["Strict-Transport-Security"]
    C --> E["Content-Security-Policy"]
    C --> F["X-Content-Type-Options"]
    C --> G["X-Frame-Options"]
    C --> H["Set-Cookie
HttpOnly, Secure, SameSite"] D --> I["Resource Server"]

Code Examples

Example 1: Security Header Middleware

from flask import Flask, request, jsonify, make_response

app = Flask(__name__)

SECURITY_HEADERS = {
    'Strict-Transport-Security': 'max-age=31536000; includeSubDomains; preload',
    'X-Content-Type-Options': 'nosniff',
    'X-Frame-Options': 'DENY',
    'X-XSS-Protection': '0',
    'Referrer-Policy': 'strict-origin-when-cross-origin',
    'Permissions-Policy': 'geolocation=(), microphone=(), camera=()',
    'Cache-Control': 'no-store'
}

@app.after_request
def add_security_headers(response):
    for header, value in SECURITY_HEADERS.items():
        response.headers[header] = value
    return response

@app.route('/api/protected')
def protected_route():
    # Token validation happens here
    return jsonify({'message': 'Protected resource'})
from flask import make_response, jsonify
import jwt
from datetime import datetime, timedelta, timezone

def issue_tokens(user_id, private_key):
    access_token = create_access_token(user_id, private_key)
    refresh_token = create_refresh_token(user_id, private_key)

    response = make_response(jsonify({
        'access_token': access_token,
        'token_type': 'Bearer',
        'expires_in': 900
    }))

    # Set refresh token as httpOnly cookie
    response.set_cookie(
        'refresh_token',
        value=refresh_token,
        httponly=True,
        secure=True,
        samesite='Strict',
        max_age=7 * 24 * 3600,
        path='/api/auth/refresh',
        domain='.dodatech.com'
    )

    # Add cache control for token responses
    response.headers['Cache-Control'] = 'no-store'
    response.headers['Pragma'] = 'no-cache'
    return response

# Usage
response = issue_tokens('user_123', private_key)
# Cookie is automatically httpOnly, Secure, SameSite=Strict

Example 3: CSP for Token Protection

def generate_csp():
    script_src = ["'self'", "'strict-dynamic'", "'nonce-{random}'"]
    connect_src = ["'self'", "https://api.dodatech.com"]
    form_action = ["'self'"]
    base_uri = ["'none'"]

    csp = "; ".join([
        f"default-src 'self'",
        f"script-src {' '.join(script_src)}",
        f"connect-src {' '.join(connect_src)}",
        f"form-action {' '.join(form_action)}",
        f"base-uri {' '.join(base_uri)}",
        "object-src 'none'",
        "frame-ancestors 'none'"
    ])
    return csp

@app.after_request
def add_csp(response):
    nonce = secrets.token_hex(16)
    csp = generate_csp().replace('{random}', nonce)
    response.headers['Content-Security-Policy'] = csp
    return response

# The CSP prevents token exfiltration by restricting
# where content can be loaded from and sent to

Common Mistakes

1. Missing HSTS Header

Without HSTS, an attacker with network access can downgrade HTTPS to HTTP and steal tokens.

2. CSP with 'unsafe-inline'

Inline scripts can bypass CSP. Use nonces or hashes instead of 'unsafe-inline'.

3. Cookies Without SameSite Attribute

Without SameSite=Strict, CSRF Attacks can use the user's cookies to make authenticated requests.

4. Sending Tokens in URL Parameters

URLs are logged by proxies and servers, exposing tokens. Always send tokens in headers.

5. Caching Token Responses

Set Cache-Control: no-store on any endpoint that returns tokens.

Practice Questions

  1. What does Strict-Transport-Security prevent?
  2. Why should refresh tokens be in httpOnly cookies?
  3. What CSP directive controls where API requests can be sent?
  4. How does SameSite=Strict prevent CSRF?
  5. Why disable caching for token responses?

Answers:

  1. Prevents SSL stripping attacks by forcing browsers to always use HTTPS.
  2. httpOnly cookies are inaccessible to JavaScript, preventing XSS-based token theft.
  3. connect-src controls which URLs JavaScript can make network requests to.
  4. The browser only sends SameSite=Strict cookies for same-site requests, blocking cross-site request forgery.
  5. Cached token responses could be served from disk to an attacker with local access.

Challenge: Configure a complete security headers middleware for a JWT-protected API. Include HSTS preload, CSP with nonces, and secure cookie configuration. Test with securityheaders.com.

FAQ

What is the most important security header?

: Strict-Transport-Security. Without it, all other protections are worthless if an attacker can strip HTTPS.

Should I use `X-XSS-Protection`?

: No. Modern browsers deprecated it. Use CSP instead.

Can CSP prevent all token theft?

: No, but it raises the bar significantly. Combine CSP with httpOnly cookies and short token lifetimes.

What is the `Referrer-Policy` header for?

: It controls how much referrer information is sent with requests, preventing token leaks in Referer headers.

How do I test my security headers?

: Use securityheaders.com or curl -I to inspect response headers.

What's Next

Apply these headers to your {{< ilink "JWT" "JWT Authentication Service" }}, and review {{< ilink "JWT" "JWT Best Practices" }} for additional production hardening.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro