How HTTP Stays Secure: Cookies, HTTPS, and TLS Explained

HTTP is stateless and by default sends everything as plain text. If you are not familiar with how HTTP works, how requests and responses are structured, and what methods and status codes mean, that is covered here.
Those two facts - statelessness and plain text - create two separate problems worth understanding: how do you maintain any sense of identity across requests that have no memory of each other, and how do you stop anyone intercepting those requests from reading everything inside them?
Cookies solve the first problem. HTTPS and TLS solve the second.
Cookies: giving HTTP a memory it was not born with
Because every HTTP request arrives with no context, the server has no built-in way of knowing whether the person making this request is the same one who logged in thirty seconds ago. Cookies are how that problem gets solved.
When a server wants to store a small piece of information on the client, it sends a Set-Cookie header in its response:
Set-Cookie: session_id=abc123; HttpOnly; Secure; SameSite=Lax
The browser sees that header, stores the cookie, and from that point automatically attaches it to every subsequent request to that same domain:
Cookie: session_id=abc123
The server reads that cookie on every request, looks up what abc123 corresponds to in its records, and suddenly knows who it is talking to. The protocol is still stateless. The experience is not.
The attributes after the cookie value are not optional decoration. They are security controls.
HttpOnly means JavaScript running on the page cannot access this cookie. This matters because of a type of attack called XSS, or Cross-Site Scripting, where malicious code gets injected into a page and tries to steal cookies. If an attacker manages to run JavaScript on your site, HttpOnly cookies are invisible to it. Any cookie that holds a session ID or authentication token should almost always have this attribute.
Secure means the browser will only send this cookie over HTTPS, never over plain unencrypted HTTP. Without this, a cookie could be exposed if traffic is intercepted on an unencrypted connection.
SameSite controls whether the browser sends the cookie when a request originates from a different website. This protects against CSRF, Cross-Site Request Forgery, where a malicious website tricks your browser into making a request to another site using your existing cookies.
A concrete example of how CSRF works: you are logged into your bank. Your browser has a session cookie for that bank. You then visit a malicious website. That site has a hidden form that silently submits a request to your bank's transfer endpoint. Your browser, following normal rules, would attach your bank's cookie to that request automatically. The bank would see a valid session and process the transfer.
SameSite breaks this by telling the browser not to send the cookie on requests that originate from other sites. Lax is the sensible default, cookies are sent on normal navigation but blocked on cross-site form submissions. Strict blocks even more. None allows everything but requires the Secure attribute to be set.
Why plain HTTP is not enough
HTTP sends everything as readable text. Every request, every response, every cookie, every password typed into a form, all of it travels across the network as plain text that anyone positioned between your browser and the server can read.
On a public Wi-Fi network, that is a real and practical risk. Without encryption, traffic can be intercepted, read, and modified without either side knowing.
HTTPS fixes this by running HTTP inside TLS, Transport Layer Security. TLS is an encryption layer that sits between your application and the TCP connection. Your HTTP data goes in, gets encrypted, travels across the network as scrambled data that is meaningless without the right keys, arrives at the other end, and gets decrypted.
TLS gives you three guarantees:
Confidentiality - traffic is encrypted. Intercepted packets are unreadable.
Integrity - traffic cannot be secretly modified in transit. If anyone tampers with an encrypted packet, it becomes invalid and the recipient knows something went wrong.
Authentication - the server proves it is actually who it claims to be. When you connect to your bank, you need to know you are talking to your actual bank and not someone sitting in the middle pretending to be it.
How a connection actually gets established
To understand how TLS works, it helps to first understand what happens at the TCP level, since TLS builds on top of it.
When your browser wants to connect to a server, it does not just start firing data immediately. First, both sides confirm they can hear each other through what is called the three-way handshake:
Browser → Server: "I want to connect"
Server → Browser: "Got it, can you hear me?"
Browser → Server: "Yes, let's go"
Only after this completes does any data start moving. Every connection starts here, whether it is HTTP or HTTPS.
For HTTPS, the TCP handshake is just the beginning. Once the connection is open, TLS runs its own handshake on top of it before any HTTP data flows.
The TLS handshake
The TLS handshake is where encryption gets established and the server proves its identity. Here is what actually happens.
The server sends its certificate. A certificate is a document that says "I am myblog.com, here is my public key, and this has been verified by a trusted third party." That trusted third party is called a Certificate Authority, or CA. Companies like Let's Encrypt, DigiCert, and Comodo act as CAs. Their job is to verify that a certificate genuinely belongs to who it claims to belong to and to sign it accordingly. Your browser and operating system come pre-loaded with a list of CAs they trust.
Your browser checks the certificate's signature against that list. If the certificate was signed by a trusted CA and the domain matches what you typed, the certificate is valid. If the certificate is expired, self-signed, or the domain does not match, your browser throws a warning.
Once the certificate is verified, both sides use it to establish a shared secret, a randomly generated encryption key that only they know, without ever sending that key directly over the network. All traffic from this point is encrypted with that key.
The full sequence for any HTTPS request:
1. TCP handshake → connection established
2. TLS handshake → encryption established, server identity verified
3. HTTP request → your actual data, now encrypted
4. HTTP response → server's response, also encrypted
That is a significant amount of back-and-forth before any real data moves. Modern TLS versions and HTTP/2 have reduced this overhead considerably, but the handshakes still happen on every new connection.
Where HTTPS actually gets handled in production
Your Node or Python application does not usually handle TLS directly. In production, there is typically a reverse proxy sitting in front of your application that handles the encrypted connection with the outside world, decrypts the traffic, and forwards plain HTTP to your app internally.
A reverse proxy is a server that sits in front of your application and forwards requests to it. It handles TLS so your application code does not have to. This separation is called TLS termination, the encrypted connection ends at the proxy, not at your application.
Client (HTTPS) → Reverse proxy (handles encryption) → Your app (plain HTTP internally)
Tools like Nginx are commonly used as reverse proxies. Cloud platforms like AWS, Render, and Railway run one automatically in front of your deployed application.
This also explains how custom domains get HTTPS.
If you own my-website.com through a registrar like GoDaddy, the domain registrar's only job is managing your DNS records. You point those records at wherever your app is hosted and HTTPS is handled at that destination, not by GoDaddy.
On managed platforms like Render or Railway, you add your domain in their dashboard, update your DNS records to point at their servers, and they automatically provision and renew an SSL certificate for you. Nothing else to configure.
On a raw server like a DigitalOcean droplet, you set up Nginx yourself, install a free tool called Certbot which talks to Let's Encrypt to generate your certificate, and configure Nginx to use it. Let's Encrypt is a free Certificate Authority that automated the entire process of getting a certificate, which is why HTTPS is now standard practice across even the smallest projects.
A load balancer , which distributes incoming traffic across multiple servers, is essentially a reverse proxy doing an extra job. All load balancers are reverse proxies. Not all reverse proxies are load balancers. You do not need multiple servers to benefit from a reverse proxy. A single application still benefits from having one handle TLS, compression, and certificate renewal.
See it for yourself
Open your browser and go to https://httpbin.org. Click the padlock icon in the address bar. You will see the certificate details: who it was issued to, which Certificate Authority signed it, and when it expires. That is the certificate your browser verified during the TLS handshake before this page loaded.
Now open DevTools with F12, go to the Network tab, and reload the page. Click any request and look at the Headers section. Find the response header Set-Cookie if any are present, and look at the attributes, HttpOnly, Secure, SameSite, next to the cookie values.
Then try this in your terminal:
bash
# The -v flag shows the full connection including TLS details
curl -v https://httpbin.org/get
Look for lines near the top of the output that mention TLS. You will see the TLS version negotiated, the certificate details, and the cipher suite both sides agreed on. All of that happened before a single byte of HTTP data was exchanged.
To see a Set-Cookie header in action:
bash
curl -i https://httpbin.org/cookies/set?name=jordan
The -i flag shows response headers. You will see a Set-Cookie header in the response and a 302 redirect status code, which is httpbin redirecting you to a page that shows the cookie it just set. That is the full cookie flow in one command.
Between these two posts, the full HTTP picture is now in place. Requests and responses have structure, methods carry intent, status codes communicate outcomes, headers carry context, cookies maintain identity across a stateless protocol, and TLS ensures none of it can be read or tampered with in transit. Everything your backend sends and receives lives inside this system.



