Dev Learning Lab

The labs on this site need JavaScript to run. The same content is written out below.

Fundamentals › Networking

HTTPS

How is HTTPS different from HTTP?

HTTP sends requests and responses onto the network as plain characters. Between the client and the server there are things like routers and carrier equipment, and if someone looks at the characters passing through there, even a password can be read as it is. HTTPS sends the same HTTP characters encrypted, and the address starts with https://. The code box on the screen has three parts. The first is the address the client connects to, the second is the content it sends (the body of a POST request), and the third is the server's certificate, which appears only for https. Before encryption starts, the server shows its certificate to prove that it is the owner of that name, and the client checks two things: whether the certificate is in its list of trusted certificates, and whether the name it connected to is written in the certificate. If a check fails, the client closes the connection and does not send the request. In this lab you change https:// to http://, stop trusting the certificate, or connect with a name that is not written in the certificate, and send the same content each time. You see what can be seen in the middle of the diagram and what the server receives. 8080 and 8443 on the screen are port numbers written as examples, and password=1234 is a string for testing.

Checked against real execution · Ran 8 combinations between an http/https server and client of Node.js 24.18.0 with a TCP relay placed between them, using a test certificate made with openssl, and checked them

Concepts in this lab

HTTPS and TLS
HTTPS is HTTP communication encrypted with TLS. TLS keeps third parties from seeing the content (confidentiality), keeps it from being changed on the way (integrity), and uses a certificate to check that the server you connected to is the real one (authentication). SSL is the protocol that came before TLS. It is now deprecated and only TLS is actually used, but the name SSL is still used alongside it by habit.
TLS handshake and certificates
The TLS handshake is the process, before encrypted communication starts, in which the client and the server agree on the cryptographic algorithms to use, verify the identity of the server, and agree on the keys that will encrypt the data. A certificate is a document that contains the server's public key and domain information and is signed and vouched for by a trusted certificate authority (CA). The client validates it to confirm that the server it connected to is the real one.
Plaintext
Plaintext means characters that are sent as they are, without encryption. When you connect with http://, the characters of the request and the response travel over the network as plaintext, so they can be read at places between the client and the server such as a router or carrier equipment, and a password is no exception. When data is encrypted, the same content travels as bytes that cannot be recognized, and only the two ends that hold the key can turn it back into the original characters (RFC 8446 section 1 explains that the data in a channel made with TLS is visible only at the two ends). The word plaintext can also mean "the text before it is encrypted," so tell the two apart from the context.
Server certificate verification (trust anchors and name)
An HTTPS client checks the certificate the server shows before it sends the encrypted request. There are two checks. One is whether the certificate can be trusted: RFC 9110 section 4.3.4 says this check needs the client to be configured in advance with a set of trust anchors, that is, a list of the certificates (or certificate authorities) it trusts. The other is whether the name it connected to (or the IP address, if it connected by IP address) is written in the certificate. For example, if you connect by 127.0.0.1 to a server that shows a certificate with only the name localhost, the name is not written there, so the check fails. When a certificate is not valid for that address, a user agent such as a browser must obtain confirmation from the user or terminate the connection, and an automated client is recommended to terminate the connection (same section).

Try these

Running with the default settings

  1. The address to connect to the server is https://localhost:8443/login. It starts with https://, so before sending HTTP characters, the client (the program that connects to the server) first verifies the server and prepares encryption. Between the client and the server there are things like a router and carrier equipment (the middle of the diagram), and someone can look at the characters passing through there. 8443 is a port number written as an example.
  2. The server shows its certificate. A certificate is a document with which the server proves "I am the owner of this name," and this server's certificate has the name localhost written in it ("Server certificate" in the code box).
  3. The client checks the certificate. The first question is whether this certificate can be trusted. The client has a list of trusted certificates, and it looks to see whether the certificate the server showed is in that list. This certificate is in the list the client trusts, so it passes.
  4. The second question is whether the name connected to is written in the certificate. The name connected to is localhost. The only name written in the certificate is localhost. The name connected to is written there, so it passes.
  5. Both checks passed. The two sides start encrypted communication, having agreed on the key used to encrypt the HTTP characters. How the key is agreed on is covered in the in-depth lab "TLS handshake".
  6. The client encrypts the HTTP request characters (the POST /login request and the body password=1234) and sends them. The characters before encryption are the same as when sending with http://.
  7. If you look at the bytes passing through the middle, you see unreadable bytes. You cannot find either POST /login or password=1234 as characters. This is the result of searching the bytes recorded by the relay placed in the middle in this run. In this run, the bytes that start the connection were seen to contain the name localhost, which the client connected to, as plain characters. That is not the content of the request.
  8. The server decrypts the bytes it received with the key and gets the original characters. The body that the server's request-handling function received is password=1234. Encryption changes only what is seen in the middle; it does not change the characters the server receives.

Result of this run

Code

# Address the client connects to
https://localhost:8443/login

# Content sent (body of the POST request)
password=1234

# Server certificate
Name: localhost
Signed by an authority the client trusts

Assumptions of this model

We ran all eight combinations for real, using only 127.0.0.1 on the PC used to write this lab, and checked against the results. We did not connect to any external network. The server is the http server or https server of Node.js 24.18.0 (its request-handling function records the body it received), and the client is http.request or https.request of the same Node.js. Between the server and the client we placed a TCP relay made by the Probe. The relay passes the bytes it receives to the other side as they are, while recording the bytes in both directions, and the "what can be seen in the middle (router, carrier equipment, and so on)" in the middle of the diagram was reproduced from this record. In all of the recorded bytes, we searched for the strings password=1234 and POST /login. In the four combinations that connect with http://, both strings were there, and in the combinations that connect with https://, both strings were absent, both for those that connected and for those that were closed. This is the result of the search in this run, and it is not proof that the encryption is safe. Also, in the combinations that connect to localhost with https://, the string localhost was visible in the bytes that start the connection, and in the combinations that connect to 127.0.0.1 it was not (we only observed this and did not check the cause in this lab). We made the certificate for testing with openssl 3.5.7 on the PC used to write this lab (self-signed, name localhost, subjectAltName only DNS:localhost, valid for 2 days, files in verify/out/https_basic/). "The client trusts it" was reproduced by giving this certificate to the ca option of https.request, and "does not trust it" by not giving the ca option (so that only the list of certificate authorities included in Node.js is trusted); we did not use rejectUnauthorized: false, which turns verification off. The two combinations that do not trust it were closed with the error DEPTH_ZERO_SELF_SIGNED_CERT, and the combination that trusts it but connects to 127.0.0.1 was closed with the error ERR_TLS_CERT_ALTNAME_INVALID (the message starts with Hostname/IP does not match certificate's altnames). In the combination where both apply (does not trust it and connects to 127.0.0.1), the untrusted certificate error came out, and the name mismatch error did not. In the combinations that were closed, the server's request-handling function was never called (the server recorded only the error ECONNRESET, meaning the connection was reset), and in the combinations that connected, the server received the body password=1234 of the POST /login request. The TLS version used by the combinations that connected was TLSv1.3, and the cipher suite was TLS_AES_256_GCM_SHA384; we could not measure the TLS version of the combinations that were closed. 8080 and 8443 on the screen are example numbers, and in the real runs we used free ports, and password=1234 is a string for testing. Real browsers and operating systems come with a list of trusted certificate authorities built in, and the server's certificate must be signed by one of those authorities, but this lab does not cover that and covers only "in the trusted list / not in the list". We did not run it with a real browser. What we checked in the documents is section 4.2.2 of RFC 9110 (the https URI scheme: a connection protected by TLS, the server is authenticated as acting on behalf of the authority, default port 443) and section 4.3.4 (https certificate verification: the identity in the certificate must be checked against the server at the address that was connected to, and the check needs a list of trusted certificates. If the certificate is not valid for that address, a user agent such as a browser must obtain confirmation from the user or terminate the connection, and for an automated client terminating the connection is recommended), and section 1 of RFC 8446 (the purpose of TLS is a secure channel between two peers, the data in the channel is visible only at the two ends, and tampering can be detected). The client in this lab is an automated program, so it closes the connection without asking the user. We did not cover the case where a browser shows a warning page and asks whether to continue. We did not cover how the key is agreed on before encryption, the order of the messages in the TLS handshake, or the validity period, revocation, and chain of certificates; those are covered in the in-depth lab "TLS handshake". The code on the screen shows the address the client connects to, the content it sends, and the state of the server's certificate, and it is not the program code of a server or a client.

Previous lab: JSON · Next lab: Proxy servers

Concepts in this lab

What this lab shows

Settings

Code


      
    

Diagram

Run comparison

Try these