All posts
Engineering 7 min read 0

Encrypting request payloads when HTTPS isn't the whole story

TLS protects data in transit, but it doesn't stop a payload from being read the moment it lands somewhere you didn't expect — logs, proxies, or a compromised intermediary.

August 23, 2026#Infrastructure as Code#Backend Development
Encrypting request payloads when HTTPS isn't the whole story

HTTPS is often treated as the finish line for 'secure enough,' and for most requests it is. But TLS only guarantees the data is unreadable between client and server — it says nothing about what happens once the payload lands. It can still show up in access logs, get captured by a debugging proxy, or pass through an intermediary service that has no business seeing the raw contents. That gap is what pushed me to add an application-level encryption layer on top of TLS for sensitive payload fields, rather than trusting transport security alone. The approach was straightforward in principle: encrypt sensitive fields client-side using a symmetric key (AES-GCM) before the request ever leaves the browser, and decrypt server-side after the request passes through nginx and hits the NestJS handler. The GCM mode mattered specifically because it gives you both confidentiality and integrity — an authentication tag that fails decryption if the payload was tampered with in transit, which a plain CBC cipher wouldn't catch on its own. The harder part wasn't the cryptography, it was key management: hardcoding a shared secret defeats the purpose, so I settled on a short-lived session key exchanged once at authentication and cached server-side, rotated on token refresh. The result is that even if a payload ends up somewhere unintended — a misconfigured log statement, a proxy with verbose debug mode on — what's captured is ciphertext, not the actual data. It's not a replacement for TLS, it's a second layer for the handful of fields where 'nothing downstream should ever see this in plaintext' is a requirement, not a nice-to-have.

Thanks for reading.

Back to all posts