Middleware Performance Trap: When Authentication consumes your server resources!

Middleware Performance Trap: When Authentication consumes your server resources!

You've optimized your database queries, tuned your cache, and your business logic runs in milliseconds. But your API still feels slow. The culprit? Middleware runs before your code even executes.

Authentication, authorization, and CORS middleware processes every single request. If they're slow, everything is slow, including endpoints that return "Hello World."


𝗧𝗵𝗲 𝗛𝗶𝗱𝗱𝗲𝗻 𝗖𝗼𝘀𝘁

Typical authentication middleware on each request:

  • May validate JWT against external auth server (~150ms)
  • Loads user from database (50ms)
  • Checks user status and loads roles (75ms)
  • Verifies permissions (50ms)

𝗧𝗼𝘁𝗮𝗹: 𝟯𝟮𝟱𝗺𝘀 𝗯𝗲𝗳𝗼𝗿𝗲 𝘆𝗼𝘂𝗿 𝗰𝗼𝗻𝘁𝗿𝗼𝗹𝗹𝗲𝗿 𝗿𝘂𝗻𝘀. At 100 requests/second, that's 32.5 seconds of wasted CPU time.


𝟭. 𝗩𝗮𝗹𝗶𝗱𝗮𝘁𝗲 𝗧𝗼𝗸𝗲𝗻𝘀 𝗟𝗼𝗰𝗮𝗹𝗹𝘆

If you are using an external auth server, stop calling it on every request. JWT tokens are self-contained; verify the signature locally using cached public keys. Token validation drops from 150ms to under 1ms.

𝟮. 𝗖𝗮𝗰𝗵𝗲 𝗨𝘀𝗲𝗿 𝗗𝗮𝘁𝗮 𝗔𝗴𝗴𝗿𝗲𝘀𝘀𝗶𝘃𝗲𝗹𝘆

After validating a token, middleware often loads user details from the database; every single time. Cache user data (name, email, roles, status) for 5-15 minutes using the user ID as the key.

Better yet: embed critical info (user ID, roles, status) directly in the JWT token. Eliminates database queries entirely. When roles change, users get updated permissions on next login with a fresh token, but take care of the token size and max HTTP header size!

𝟯. 𝗦𝗸𝗶𝗽 𝗔𝘂𝘁𝗵𝗲𝗻𝘁𝗶𝗰𝗮𝘁𝗶𝗼𝗻 𝗪𝗵𝗲𝗻 𝗡𝗼𝘁 𝗡𝗲𝗲𝗱𝗲𝗱

Global authentication middleware runs on every endpoint, even public ones that don't need it. This is wasteful.

Apply authentication only to routes that require it. Public endpoints (product listings, search, homepage) should skip authentication entirely; zero overhead. Design middleware to short-circuit immediately for anonymous endpoints when no token is present.

𝟰. 𝗖𝗮𝗰𝗵𝗲 𝗔𝘂𝘁𝗵𝗼𝗿𝗶𝘇𝗮𝘁𝗶𝗼𝗻 𝗗𝗲𝗰𝗶𝘀𝗶𝗼𝗻𝘀

Checking permissions often involves loading user roles, evaluating hierarchies, and checking resource ownership; ~50-100ms per request.

Cache the authorization decision itself. For "Can User 123 edit Orders?", cache auth:user:123:order:edit → true/false for 5-10 minutes. Permission checks drop from 100ms to under 5ms.

This works because permissions rarely change. When they do (admin modifies roles), either use short cache TTL or invalidate on permission changes.

𝟱. 𝗖𝗮𝗰𝗵𝗲 𝗖𝗢𝗥𝗦 𝗣𝗿𝗲𝗳𝗹𝗶𝗴𝗵𝘁 𝗥𝗲𝘀𝗽𝗼𝗻𝘀𝗲𝘀

CORS preflight requests (OPTIONS) are invisible to most developers but can double your traffic. Every cross-origin request with custom headers triggers a preflight check before the actual request.

𝗧𝗵𝗲 𝗽𝗿𝗼𝗯𝗹𝗲𝗺: 10,000 API requests might actually be 20,000 total requests; 10,000 preflights + 10,000 actual requests. CORS middleware processes every single one.

𝗧𝗵𝗲 𝘀𝗼𝗹𝘂𝘁𝗶𝗼𝗻: Set Access-Control-Max-Age header to cache preflight responses. Tell browsers "this preflight is valid for 1 hour" (3600 seconds) or even 24 hours (86400 seconds).

Browsers cache the result and skip preflight requests for subsequent calls within that window. You just cut CORS-related traffic in half with one header.

𝟲. 𝗟𝗮𝘇𝘆 𝗟𝗼𝗮𝗱 𝗪𝗵𝗮𝘁 𝗬𝗼𝘂 𝗡𝗲𝗲𝗱

Don't load full user profiles in middleware unless you really need them. Authentication proves identity. Authorization proves access. Loading user preferences, settings, and profile details? Often unnecessary.



𝗧𝗵𝗲 𝗧𝗿𝗮𝗱𝗲-𝗢𝗳𝗳

Caching authentication/authorization creates a 5-15 minute window where revoked permissions might still work if the cache invalidation is not strict. For most applications, this is acceptable. For high-security systems (banking, healthcare), use shorter TTL (1-2 minutes) or don't cache critical operations.

The performance gains massively outweigh the brief inconsistency for 99% of applications.

To view or add a comment, sign in

More articles by Abbas Alabbas

Others also viewed

Explore content categories