Skip to content

How HTTPS Works

💡
Before you start

You need a terminal with openssl — nothing else, and no internet. It is already installed on macOS and almost every Linux system; on Windows it comes with Git for Windows (use the “Git Bash” terminal). Check with openssl version; any 1.1 or 3.x is fine. Everything below runs on your own machine and talks to no server — you will build a certificate and then take it apart to see what a browser checks.

What Is HTTPS?

HTTPS stands for HyperText Transfer Protocol Secure. It is the encrypted version of HTTP, the protocol your browser uses to communicate with websites. When you visit a site over HTTPS, all data exchanged between your browser and the server is encrypted, preventing anyone on the network from reading or tampering with it.

Without HTTPS, everything you send and receive travels in plain text. This includes passwords, credit card numbers, personal messages, and even the pages you view. Anyone on the same network -- whether a coffee shop Wi-Fi operator, your ISP, or a malicious actor performing a man-in-the-middle attack -- can intercept and read this data.

💡
HTTP vs. HTTPS

HTTP transmits data in plain text on port 80. HTTPS wraps that same HTTP protocol inside a TLS (Transport Layer Security) encrypted tunnel on port 443. The underlying HTTP protocol is identical -- HTTPS simply adds encryption around it.

You can tell a site is using HTTPS by looking at the URL bar. The address will start with https:// and most browsers display a padlock icon next to it. Clicking the padlock shows certificate details, including who issued it and when it expires.

The TLS Handshake Process

Before any encrypted data can flow, your browser and the server must agree on how to encrypt the connection. This negotiation is called the TLS handshake. It happens in milliseconds, completely behind the scenes, every time you connect to an HTTPS site.

Step-by-Step Handshake (TLS 1.3)

Modern browsers use TLS 1.3, which streamlined the handshake to just one round trip:

1
Client Hello: Your browser sends a message to the server listing the TLS versions and cipher suites it supports, along with a random number and its key share (Diffie-Hellman public parameters).
2
Server Hello: The server picks the strongest mutually supported cipher suite, sends its own random number and key share, and presents its TLS certificate to prove its identity.
3
Key Derivation: Both sides independently compute the same session key using the exchanged Diffie-Hellman parameters. Neither side ever sends the actual key over the wire -- they derive it mathematically.
4
Encrypted Communication: Both sides confirm the handshake is complete and all subsequent data is encrypted with the shared session key using symmetric encryption.

The entire handshake in TLS 1.3 completes in a single round trip (1-RTT), compared to two round trips in TLS 1.2. This makes HTTPS connections faster than ever before.

Certificate Authorities

When a server presents its TLS certificate during the handshake, your browser needs to verify that the certificate is legitimate. This is where Certificate Authorities (CAs) come in.

A Certificate Authority is a trusted organization that verifies the identity of website owners and issues digital certificates. Your operating system and browser ship with a pre-installed list of trusted CAs -- called the trust store. When a server presents a certificate signed by one of these CAs, your browser trusts it automatically.

The Chain of Trust

Certificates form a chain:

  • Root CA Certificate -- Pre-installed in your OS/browser trust store. These are the ultimate anchors of trust. Examples include DigiCert, Let's Encrypt (ISRG Root), and GlobalSign.
  • Intermediate CA Certificate -- Signed by the root CA. Most website certificates are issued by intermediate CAs, not directly by root CAs. This limits exposure if an intermediate key is compromised.
  • Server Certificate -- The certificate presented by the website, signed by the intermediate CA. Contains the site's domain name, public key, validity dates, and the issuer's signature.

Your browser walks up this chain: it verifies the server certificate was signed by a valid intermediate, that the intermediate was signed by a trusted root, and that none of the certificates have expired or been revoked.

⚠️
Self-Signed Certificates

A self-signed certificate is not signed by any CA -- it is signed by the server itself. Browsers will show a security warning because they cannot verify the server's identity. Self-signed certificates are acceptable for internal services and development, but should never be used on public-facing websites.

Symmetric vs. Asymmetric Encryption

HTTPS uses both types of encryption, each for a different purpose. Understanding why requires knowing their strengths and weaknesses.

Asymmetric Encryption (Public Key Cryptography)

Asymmetric encryption uses a pair of keys: a public key that anyone can have, and a private key that only the owner keeps secret. Data encrypted with the public key can only be decrypted with the private key, and vice versa.

This solves the key distribution problem -- you can share your public key openly without compromising security. However, asymmetric encryption is computationally expensive and slow, making it impractical for encrypting large amounts of data.

Symmetric Encryption

Symmetric encryption uses a single shared key for both encryption and decryption. It is vastly faster than asymmetric encryption -- typically 100 to 1,000 times faster. The problem is that both sides need to have the same key, and you cannot safely send a key over an insecure channel.

How HTTPS Combines Both

HTTPS uses asymmetric encryption during the TLS handshake to securely establish a shared session key. Once both sides have the session key, all subsequent communication uses fast symmetric encryption. This gives you the best of both worlds: secure key exchange plus fast bulk encryption.

# Common cipher suite in TLS 1.3:
TLS_AES_256_GCM_SHA384

# Breakdown:
# AES_256_GCM  = Symmetric encryption (fast, for data)
# SHA384       = Hash function (for integrity verification)
# Key exchange = X25519 or P-256 (Diffie-Hellman, asymmetric)

How to Verify HTTPS

Knowing how to check a website's HTTPS configuration helps you assess whether your connection is truly secure. Here are practical methods anyone can use.

In Your Browser

Click the padlock icon in your browser's address bar to view:

  • Whether the connection is secure
  • The certificate issuer (CA)
  • The certificate's validity dates
  • The domain names the certificate covers

Using Command-Line Tools

For deeper inspection, use openssl from the terminal:

# View a site's certificate details
openssl s_client -connect example.com:443 -servername example.com < /dev/null 2>/dev/null | openssl x509 -noout -text

# Check certificate expiration date
openssl s_client -connect example.com:443 -servername example.com < /dev/null 2>/dev/null | openssl x509 -noout -dates

# Check the TLS version and cipher suite
openssl s_client -connect example.com:443 -servername example.com < /dev/null 2>/dev/null | grep -E "Protocol|Cipher"

Online Tools

Services like SSL Labs (ssllabs.com/ssltest) provide a detailed report grading a website's TLS configuration from A+ to F. They check for outdated protocols, weak ciphers, certificate chain issues, and known vulnerabilities.

Mixed Content Issues

Mixed content occurs when an HTTPS page loads some resources (images, scripts, stylesheets) over plain HTTP. This is a security problem because those HTTP resources can be intercepted and modified by an attacker, potentially compromising the entire page.

Types of Mixed Content

  • Passive mixed content -- Resources like images, audio, and video loaded over HTTP. Lower risk because they cannot directly execute code, but an attacker could swap an image to display misleading information.
  • Active mixed content -- Scripts, stylesheets, iframes, and other resources that can execute code or change the page structure. Modern browsers block active mixed content entirely because the risk is too high.
⚠️
Mixed content breaks the padlock.

Even one HTTP resource on an HTTPS page will cause browsers to remove the padlock or show a warning. Users lose confidence in the site's security, and the connection is genuinely weakened. Always load all resources over HTTPS.

Fixing Mixed Content

<!-- Bad: loading over HTTP -->
<img src="http://example.com/image.png">
<script src="http://cdn.example.com/lib.js"></script>

<!-- Good: loading over HTTPS -->
<img src="https://example.com/image.png">
<script src="https://cdn.example.com/lib.js"></script>

<!-- Also good: protocol-relative (inherits page protocol) -->
<img src="//example.com/image.png">

You can also use the Content-Security-Policy header to enforce HTTPS for all resources:

Content-Security-Policy: upgrade-insecure-requests;

This header tells the browser to automatically upgrade any HTTP resource requests to HTTPS before sending them, eliminating mixed content without changing your HTML.

HSTS: HTTP Strict Transport Security

Even if your site supports HTTPS, a user might type http://yoursite.com or click an old HTTP link. During that brief HTTP request before the redirect to HTTPS, an attacker could intercept the connection. This is called an SSL stripping attack.

HSTS solves this. It is a response header that tells the browser: "For the next N seconds, never connect to this domain over plain HTTP. Always use HTTPS, even if the user types http:// or clicks an HTTP link."

# HSTS header example
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload

# Breakdown:
# max-age=31536000    -- Remember this policy for 1 year (in seconds)
# includeSubDomains   -- Apply to all subdomains too
# preload             -- Eligible for browser preload lists
💡
HSTS Preloading

The preload directive allows your domain to be hardcoded into browsers' built-in HSTS lists. Once preloaded, browsers will never attempt an HTTP connection to your domain, even on the very first visit. You can submit your site at hstspreload.org. Be careful -- removing your domain from the preload list takes months.

After the browser receives an HSTS header, it stores the policy locally. For the duration specified by max-age, any attempt to connect over HTTP is automatically converted to HTTPS on the client side, before any network request is made. This completely eliminates the window of vulnerability that SSL stripping attacks exploit.

Now Do It Yourself: Five Steps

A certificate is just a signed file, and the padlock is the result of a few checks a browser runs on it. You will make your own certificate, read the fields the browser reads, prove why the browser distrusts it, and then watch it become trusted the moment you tell your machine to trust its issuer. Run the commands in order in one folder; each builds on the file the first one made. Every output below was captured with OpenSSL 3.0.

1
Make a certificate and its private key

Go: open a terminal, make a folder with mkdir tls-lab and cd tls-lab.

Do: run this one command.

openssl req -x509 -newkey rsa:2048 -nodes -keyout key.pem -out cert.pem -days 365 -subj "/CN=mysite.local"

You should see: a flurry of dots while the key is generated, then two new files — key.pem and cert.pem. The key is the secret half that never leaves your server; the certificate is the public half you hand to every visitor. -nodes means the key is not password-protected (fine for a lab), -x509 makes a finished certificate rather than a request to a real authority, and -subj "/CN=mysite.local" names who it is for. You just did, in one line, what a real deployment does with a genuine certificate authority.

If not: command not found means OpenSSL is not on your PATH — on Windows, open the “Git Bash” terminal rather than PowerShell. If it complains about the -subj string, keep the quotes exactly as shown; the leading slash is required.

2
Read what the browser reads

Go: same folder.

Do: run this to print the certificate’s key fields.

openssl x509 -in cert.pem -noout -subject -issuer -dates

You should see: four lines — subject=CN = mysite.local, issuer=CN = mysite.local, and a notBefore/notAfter pair one year apart. These are exactly the facts a browser checks before it shows a padlock: who the certificate is for (subject), who vouches for it (issuer), and when it is valid. An expired notAfter is the single most common cause of a browser security warning, and it is right there in plain text.

If not: unable to load certificate means you are not in the folder from step 1, or cert.pem did not get written — run ls and check it is there before going on.

3
Spot the self-signed tell

Go: same folder.

Do: look at the subject and issuer together.

openssl x509 -in cert.pem -noout -subject -issuer

You should see: subject and issuer are the same — both CN = mysite.local. That is the definition of a self-signed certificate: it vouches for itself. A real certificate’s issuer is a certificate authority the browser already trusts — a different name from the subject — and that authority’s own certificate is signed by another, up a chain that ends at a root your browser shipped with. When subject equals issuer, the chain is one link long and leads nowhere the browser was told to trust.

If not: if the two differ, you are inspecting a different certificate — a real one you downloaded, perhaps. For this lab they must match, because you signed it yourself in step 1.

4
Prove the browser is right to distrust it — this command is meant to fail

Go: same folder.

Do: ask OpenSSL to verify the certificate against your system’s trusted authorities. This is supposed to fail — the failure is the lesson.

openssl verify cert.pem

You should see: error 18 at 0 depth lookup: self-signed certificate and verification failed. Error 18 is OpenSSL’s code for “self-signed”, and it is exactly what your browser hits when it shows “Your connection is not private”. Nothing is broken — the certificate is cryptographically fine. It simply is not vouched for by anyone the system trusts, and trust, not encryption, is what this check is about. A self-signed certificate encrypts the traffic perfectly; it just cannot prove who you are connecting to, which is half the point of HTTPS.

If not: if it prints OK, you accidentally pointed it at a trusted certificate, or added -CAfile — which is step 5, and skips ahead. Plain openssl verify cert.pem must fail for a self-signed certificate.

5
Make it trusted — by naming its issuer as an authority

Go: same folder.

Do: verify again, but this time tell OpenSSL to trust this very certificate as an authority.

openssl verify -CAfile cert.pem cert.pem

You should see: cert.pem: OK. The certificate did not change — your trust did. -CAfile cert.pem says “treat this certificate as a trusted authority”, and now the same chain that failed in step 4 succeeds. This is precisely what happens when you click “accept the risk” in a browser, or when a company installs its own internal authority on every employee’s laptop: the maths never changed, only the list of who you have decided to believe. Trust in HTTPS is a decision about issuers, layered on top of encryption that works regardless.

If not: if it still fails, check you wrote cert.pem twice — once after -CAfile (the authority to trust) and once as the certificate to check. They are the same file here only because the certificate is its own issuer.

🎉
Check yourself before moving on

Without scrolling up: in step 4 openssl verify failed, yet the certificate encrypts traffic perfectly well. What exactly was the check complaining about, and why does that still matter? Answer: it could not build a chain of trust to an authority the system knows — the certificate vouches only for itself. Encryption without verified identity means you could be talking privately to an impostor, so HTTPS needs both.

Now do it without the page: re-run step 1 but set the certificate to expire immediately with -days 0, then run the step-2 command and read the notAfter date. Predict first whether openssl verify would now fail for a different reason than in step 4 — expiry rather than self-signing — then check. Hint: a certificate can be untrusted for several independent reasons, and the error code tells you which.

Summary

HTTPS is the foundation of secure communication on the web. Here is what you learned:

  • HTTPS encrypts all traffic between your browser and the server, preventing eavesdropping and tampering
  • The TLS handshake negotiates encryption parameters and establishes a shared session key in a single round trip (TLS 1.3)
  • Certificate Authorities verify website identity through a chain of trust from root CAs to server certificates
  • HTTPS combines asymmetric encryption (for secure key exchange) with symmetric encryption (for fast data transfer)
  • You can verify HTTPS using browser tools, openssl commands, or online services like SSL Labs
  • Mixed content weakens HTTPS pages and should be eliminated by loading all resources over HTTPS
  • HSTS prevents SSL stripping attacks by forcing browsers to always use HTTPS
🎉
Well done!

You now understand how HTTPS protects web traffic from end to end. In the next tutorial, you will learn about HTTP security headers that add additional layers of protection on top of HTTPS.