Zero-trust API security for Next.js and FastAPI: JWT revocation, rate limiting, and mTLS
Stateless JWTs cannot be revoked cleanly, API keys get leaked into client bundles, and naive IP rate limiting breaks behind Cloudflare. Here is the defense-in-depth security architecture we deploy for enterprise endpoints.
Most web security vulnerabilities in production do not come from zero-day exploits in cryptographic libraries. They come from predictable architecture shortcuts: issuing long-lived stateless JWTs that cannot be invalidated on logout, relying on client IP headers for rate limiting behind reverse proxies, and allowing internal microservices to communicate over unauthenticated plain HTTP. This guide details our zero-trust API architecture.
1 · Hybrid JWT revocation with Redis blocklists
Pure stateless JWTs are an operational hazard: if a user changes their password or reports a compromised session, an active token remains valid until expiry. Pure database-backed sessions defeat the performance benefit of JWTs. The hybrid solution pairs short-lived access tokens (15 minutes) with a Redis token revocation blocklist. The API gateway verifies the cryptographic signature locally, and only checks Redis if the user or session ID is flagged.
// Next.js middleware token verification with revokability check
import { jwtVerify } from "jose";
import { Redis } from "@upstash/redis";
const redis = Redis.fromEnv();
const JWT_SECRET = new TextEncoder().encode(process.env.JWT_SIGNING_SECRET!);
export async function verifyAuthToken(token: string) {
// 1. Local cryptographic signature and expiry check (0ms network)
const { payload } = await jwtVerify(token, JWT_SECRET, {
algorithms: ["HS256"],
issuer: "auth.chimbuk.com",
});
const jti = payload.jti as string;
const userId = payload.sub as string;
// 2. High-speed Redis check: has this token or user session been invalidated?
const isRevoked = await redis.exists(`revoked:jti:${jti}`, `revoked:user:${userId}`);
if (isRevoked) {
throw new Error("Token has been revoked");
}
return payload;
}2 · Token bucket rate limiting that survives proxies
Relying on req.ip for rate limiting is dangerous: malicious actors spoof the X-Forwarded-For header, while legitimate users on mobile carrier CGNAT networks share the same IP. Our rate-limiting strategy combines authenticated identity (user_id / api_key) with a cryptographically signed client fingerprint for anonymous traffic, backed by Redis sliding window scripts.
# FastAPI sliding window rate limiter with Redis
from fastapi import Request, HTTPException, Security
from fastapi.security import HTTPBearer
import redis.asyncio as aioredis
import time
redis_client = aioredis.from_url("redis://localhost:6379", decode_responses=True)
async def check_rate_limit(request: Request, limit: int = 100, window_secs: int = 60):
# Prefer authenticated subject, fallback to trusted Cloudflare CF-Connecting-IP
identifier = request.state.user_id if hasattr(request.state, "user_id") else request.headers.get("cf-connecting-ip")
if not identifier:
identifier = request.client.host
current_ts = int(time.time())
key = f"ratelimit:{identifier}:{current_ts // window_secs}"
count = await redis_client.incr(key)
if count == 1:
await redis_client.expire(key, window_secs)
if count > limit:
raise HTTPException(
status_code=429,
detail="Rate limit exceeded. Please back off.",
headers={"Retry-After": str(window_secs)}
)3 · Mutual TLS (mTLS) for inter-service communication
In a microservices architecture, network perimeter security is insufficient. If an attacker gains code execution in one container, unauthenticated internal HTTP allows lateral movement across the entire infrastructure. Every inter-service RPC must use mutual TLS (mTLS) where both the client and server present x509 certificates verified against an internal private CA.
Security in depth means assuming every perimeter will be breached. Short-lived tokens, revokable state, robust rate limits, and encrypted authenticated internal channels make a partial breach survivable.
Field notes authored by the core engineering and systems team at Chimbuk. We build and maintain production Flutter mobile applications, Next.js web architectures, and cloud backends. No recycled marketing theory — only architecture decisions, code patterns, and performance budgets from real production deployments.
Continue Reading
Related Engineering Guides
We architect and build the systems we write about.
From vector search pipelines to high-concurrency payment gateways — schedule a technical discovery call.
