SSL/TLS Concepts #
Before we move into the technical configuration in Nginx, we must understand the basic principles underlying the SSL/TLS protocol. Without a solid conceptual understanding, configuration parameters like ssl_certificate, ssl_protocols, ssl_ciphers, and ssl_session_cache will only look like memorized lines of code. A deep understanding of what happens behind an HTTPS connection helps us design a server architecture that is not only safe from cyber attacks, but also performs optimally and responsively for users.
In this article, we’ll thoroughly dissect web security architecture: from the evolution of the SSL protocol to TLS, how hybrid cryptography works, a detailed visualization of the TLS 1.2 and TLS 1.3 handshake flows, the digital certificate validation mechanism through the Chain of Trust, to the importance of the SNI (Server Name Indication) extension.
SSL vs TLS: Clearing Up the Terminology Confusion #
In everyday conversation, we often hear the term “SSL” used to refer to the web connection security protocol. Even in Nginx, the configuration directives we use still carry the ssl name (for example listen 443 ssl or ssl_protocols). But technically, the original SSL (Secure Sockets Layer) protocol is dead and no longer used because it has fatal security flaws.
Let’s look at a brief history of this protocol’s development to understand the difference:
- SSL 1.0: Created by Netscape in the early 1990s, but never released to the public due to extremely severe security flaws.
- SSL 2.0: Released in 1995. This protocol had many design weaknesses, such as weak encryption algorithms and vulnerability to man-in-the-middle (MITM) attacks.
- SSL 3.0: Released in 1996 to fix SSL 2.0. Although it survived for quite a while, SSL 3.0 was finally declared insecure after the discovery of the POODLE vulnerability (Padding Oracle On Downgraded Legacy Encryption) in 2014. The use of SSL 3.0 is now completely banned worldwide.
- TLS 1.0: Released by the IETF (Internet Engineering Task Force) in 1999 as the successor to SSL 3.0. This is where the name changed to TLS (Transport Layer Security). Structurally, TLS 1.0 is very similar to SSL 3.0, but with some security standard improvements. This protocol is now deprecated by modern browsers.
- TLS 1.1: Released in 2006, adding protection against cipher block chaining (CBC) attacks. Like version 1.0, version 1.1 is no longer supported by modern browsers since early 2020.
- TLS 1.2: Released in 2008. This is the most widely used HTTPS working standard today. TLS 1.2 introduced flexibility in choosing encryption algorithms (cipher suites) and replaced the weak MD5/SHA-1 hash functions with SHA-256 for data integrity verification.
- TLS 1.3: Released in 2018. This is the latest version that brought revolutionary changes. TLS 1.3 removed obsolete and vulnerable cryptographic algorithms, simplified the negotiation (handshake) process to reduce connection latency, and encrypts more of the handshake process to improve privacy.
So, when we configure “SSL” in Nginx today, what we’re actually enabling is TLS v1.2 and/or TLS v1.3. Nginx keeps the ssl_* directive names purely for backward configuration compatibility.
Hybrid Cryptography: The Strength Behind HTTPS #
HTTPS uses a clever approach called hybrid cryptography. To secure data sent between a browser and a server, we need high data transfer speed as well as a safe way to share the encryption key without it being spied on by a third party.
If we only used one cryptography method, we’d face a major obstacle:
- Symmetric Cryptography: Uses the same key to encrypt and decrypt data. This method is very fast and resource-efficient. However, the main problem is: how do we securely send that key from the browser to the server at the start of the connection without anyone else intercepting it?
- Asymmetric Cryptography: Uses a pair of different keys: the Public Key (distributed to everyone) and the Private Key (kept secret on the server). Data encrypted with the Public Key can only be decrypted with the matching Private Key, and vice versa. This method is very secure for exchanging information, but very slow and computationally expensive if used to encrypt all traffic data.
HTTPS solves this problem by combining both:
- Handshake Phase (Asymmetric): At the start of the connection, the browser and server use asymmetric cryptography (like RSA or Diffie-Hellman) to verify the server’s identity and securely agree on a new random secret key. This new key is called the Session Key.
- Data Transfer Phase (Symmetric): After the Session Key is securely agreed upon, asymmetric cryptography stops. For the rest of the connection, the browser and server encrypt and decrypt data using symmetric cryptography (like AES-GCM or ChaCha20-Poly1305) with the same Session Key.
Besides encryption for confidentiality, HTTPS also uses a Message Authentication Code (MAC) or one-way hash functions (like SHA-256) to ensure data integrity. This guarantees that data sent isn’t modified along the way by an attacker (tampering attacks).
TLS Handshake: Workflow Analysis & Version Comparison #
The negotiation process at the start of an HTTPS connection is called the TLS Handshake. This is the stage where the browser and server determine which protocol version to use, choose the encryption algorithms (cipher suites), verify the server’s digital certificate, and create the Session Key.
Let’s compare in depth the workflow differences between TLS 1.2 and TLS 1.3, because these differences directly impact our web server’s latency.
TLS 1.2 Handshake (2 Round-Trip Times / 2-RTT) #
In TLS 1.2, before the first application data can be sent, the browser and server must exchange messages back and forth twice (2-RTT). This means there’s extra time overhead before a website starts loading.
Here’s the detailed flow diagram of the TLS 1.2 Handshake:
sequenceDiagram
autonumber
actor Browser as Browser (Client)
actor Server as Nginx Server
Note over Browser, Server: RTT 1: Protocol Negotiation & Key Exchange
Browser->>Server: Client Hello (TLS Version, Cipher List, Client Random)
Server->>Browser: Server Hello (Chosen TLS/Cipher, Server Random)
Server->>Browser: Certificate (Server Certificate + CA Chain)
Server->>Browser: Server Key Exchange (Ephemeral Diffie-Hellman Parameters)
Server->>Browser: Server Hello Done
Note over Browser: Browser verifies the server certificate
Note over Browser, Server: RTT 2: Session Key Creation & Finish
Browser->>Server: Client Key Exchange (Pre-Master Secret / DH Public Key)
Browser->>Server: Change Cipher Spec (Start encrypting now)
Browser->>Server: Finished (First encrypted test from the Client)
Note over Server: Server processes & computes the Session Key
Server->>Browser: Change Cipher Spec (Start encrypting now)
Server->>Browser: Finished (First encrypted test from the Server)
Note over Browser, Server: Encrypted Connection Established (Data Starts Flowing)
Browser->>Server: HTTP Request (Encrypted with Session Key)
Server->>Browser: HTTP Response (Encrypted with Session Key)In the steps above:
- Steps 1 & 2: The browser sends its capability list, and the server picks the best combination both support.
- Steps 3 & 4: The server sends its identity (certificate) and temporary key parameters for the Diffie-Hellman key exchange algorithm.
- Steps 7 & 8: The browser sends its own key part. Both parties now have enough information (Client Random, Server Random, and Pre-Master Secret) to independently generate the same Session Key without ever sending that key itself over the network.
- Steps 9 & 11: The process of verifying that the keys created by both parties match and that encryption is working correctly.
TLS 1.3 Handshake (1 Round-Trip Time / 1-RTT) #
TLS 1.3 cuts the handshake time to just one round trip (1-RTT). This 1-RTT savings is very noticeable on mobile devices or internet connections with high latency.
How does TLS 1.3 manage to cut this time? The browser no longer sends a very long list of cipher suites and waits for the server to choose. In TLS 1.3, the cipher suite choices have been drastically simplified (there are only 5 main cipher suites considered secure). The browser proactively assumes the server supports one of the most popular key exchange algorithms and immediately sends its Key Share (its asymmetric key part) in the first message (Client Hello).
Here’s the TLS 1.3 Handshake flow:
sequenceDiagram
autonumber
actor Browser as Browser (Client)
actor Server as Nginx Server
Note over Browser, Server: RTT 1: Negotiation, Key Exchange, & Verification
Browser->>Server: Client Hello (TLS Choices, Key Share / DH Guess)
Note over Server: Server processes the Key Share, creates the Session Key, & encrypts the rest of the handshake
Server->>Browser: Server Hello (Chosen Cipher & Server Key Share)
Server->>Browser: Encrypted Extensions (TLS Parameters)
Server->>Browser: Certificate (Server Certificate)
Server->>Browser: Certificate Verify (Server's digital signature proves private key ownership)
Server->>Browser: Finished (Negotiation complete)
Note over Browser: Browser verifies the certificate & computes the Session Key
Browser->>Server: Finished (Negotiation complete)
Note over Browser, Server: Encrypted Connection Established (Data Starts Flowing)
Browser->>Server: HTTP Request (Encrypted with Session Key)
Server->>Browser: HTTP Response (Encrypted with Session Key)Note that in TLS 1.3:
- Only 1-RTT needed: The server immediately sends the certificate, signature proof (Certificate Verify), and the Finished message right after receiving the Client Hello.
- Encrypted Handshake: Right after the Server Hello message is sent, the remaining handshake messages below it (certificate, extensions, etc.) are already sent encrypted using temporary keys. This prevents passive eavesdroppers from seeing which certificate the server sent, improving user privacy.
- Zero-RTT (0-RTT) Session Resumption: For users returning to our site soon, TLS 1.3 supports the 0-RTT feature. With this feature, the browser can immediately send encrypted data along with the Client Hello message without waiting for any handshake at all. However, this feature has a replay attack risk, so in Nginx we must be careful before enabling it.
Digital Certificates and the Chain of Trust #
A digital certificate is the electronic passport of our website. Without a digital certificate, encryption can still happen, but browsers won’t know whether they’re talking to our real server or a fake server belonging to an attacker impersonating us.
Digital certificates are issued by a Certificate Authority (CA). The CA acts as a trusted third party that verifies the certificate requester truly controls the domain name.
Anatomy of an X.509 Digital Certificate #
Digital certificates follow the X.509 standard. Important components include:
- Subject: The protected domain name (e.g.,
example.com). - Subject Alternative Name (SAN): A list of additional domains also protected by this certificate (e.g.,
www.example.com,api.example.com). Modern browsers now require SAN and ignore the old Common Name (CN) field due to compatibility issues. - Issuer: The name of the CA that issued and signed the certificate (e.g.,
Let's Encrypt). - Validity Period: The certificate’s validity period (start and expiry dates).
- Public Key: Our server’s public key that browsers will use during the handshake.
- Digital Signature: The CA’s digital signature created using that CA’s private key. This is authentic proof that the certificate is valid and hasn’t been modified since issuance.
Understanding the Chain of Trust #
Our browser can’t possibly store thousands of certificates from every website in the world to verify their authenticity. Instead, browsers and operating systems only store a small list of trusted CA certificates called Root CAs (e.g., DigiCert Root, Let’s Encrypt’s ISRG Root X1). This list is stored in the local Root Store on the user’s device.
When we buy or create a certificate, our server certificate (Leaf Certificate) is usually not signed directly by the Root CA for security reasons. If a Root CA key were compromised, the entire internet would be in danger. Therefore, the Root CA signs an Intermediate CA certificate, and it’s this Intermediate CA that then signs our server certificate.
The browser verifies our certificate by tracing this digital signature chain upward until it finds a Root CA it trusts.
flowchart TD
subgraph Root Store Browser
RootCA["Root CA Certificate (Trusted internally by the OS/Browser)"]
end
subgraph Sent by the Nginx Server
IntermediateCA["Intermediate CA Certificate (Signed by the Root CA)"]
LeafCert["Leaf Certificate / Server Certificate (Signed by the Intermediate CA for our domain)"]
end
RootCA -- "Signs" --> IntermediateCA
IntermediateCA -- "Signs" --> LeafCert
BrowserCheck{"Browser verifies the chain:"}
BrowserCheck -->|1. Check Leaf| LeafCert
BrowserCheck -->|2. Check Intermediate| IntermediateCA
BrowserCheck -->|3. Matches a Root?| RootCA
classDef trust fill:#10b981,stroke:#047857,color:#ffffff;
classDef serverCert fill:#1e293b,stroke:#3b82f6,color:#ffffff;
class RootCA trust;
class IntermediateCA,LeafCert serverCert;This is where the importance of our Nginx configuration lies:
When we configure ssl_certificate in Nginx, we must not only include our server certificate file. We must combine our server certificate with the Intermediate CA certificate into a single PEM-encoded file. This combined file is often called the Full Chain Certificate (e.g., fullchain.pem in Let’s Encrypt).
If we forget to include the Intermediate CA in the Nginx configuration:
- Some modern browsers with Authority Information Access (AIA) Chase features may try to download the missing Intermediate CA automatically in the background. The connection will succeed, but with added latency.
- Older browsers, mobile applications, or CLI commands like
curlwill immediately reject the connection and show a security error (like “certificate signed by unknown authority” or “untrusted connection”).
Server Name Indication (SNI): The Virtual Hosting Solution for HTTPS #
In the early days of web servers, one public IP address could only serve one HTTPS domain. This was a major limitation. For plain HTTP, Nginx could read the Host header from the HTTP request to determine which server block should handle the request. However, with HTTPS, the Host header is sent after the encrypted connection is established.
The problem: how can Nginx choose the correct SSL certificate to encrypt with, if Nginx can only read the requested domain name after the encryption is established?
Without a solution, the server would be forced to send a default certificate (which might not match the requested domain), triggering a security warning in the user’s browser.
The solution to this problem is Server Name Indication (SNI), an extension to the TLS protocol. With SNI, the browser inserts the domain name it wants to reach (e.g., api.example.com) in plaintext (unencrypted) inside the Client Hello message at the start of the handshake.
SNI Process Flow:
1. Browser --> "Client Hello" message (includes: "I want to access example.com") --> Nginx
2. Nginx --> Reads the domain name "example.com" from the outer handshake
3. Nginx --> Finds the example.com server block and loads the matching certificate
4. Nginx --> Sends the example.com certificate to the Browser in the "Server Hello" message
5. The handshake continues with the correct keys
Thanks to SNI, we can configure hundreds of different HTTPS websites, each with its own SSL certificate, on a single Nginx server with just one public IP address. Nginx handles SNI automatically in the background without needing special configuration from us, as long as the server_name directive in each server block is configured correctly.
What HTTPS Protects (and Doesn’t Protect) #
As a system administrator, we must understand HTTPS’s security limits so we don’t give users a false sense of security.
Protected (Encrypted) Traffic Parts #
HTTPS encrypts the entire application layer (Layer 7) of the OSI model. The following parts are fully encrypted and cannot be read by network eavesdroppers (like ISPs, public Wi-Fi admins, or hackers):
- URL Path: The entire path after the domain name (e.g.,
/account/settings/change-password). - Query Parameters: Search parameters in the URL (e.g.,
?token=secret&user=admin). - HTTP Headers: Including sensitive headers like session Cookies, Authorization tokens, and User-Agent.
- Request & Response Body: All data sent via POST forms, uploaded files, API JSON data, and HTML/image content returned by the server.
Open (Unprotected) Traffic Parts #
Because data must be routed through the internet from point A to point B, some metadata isn’t encrypted because internet routers need it to forward data packets:
- IP Addresses: The source (user) and destination (server) public IP addresses are clearly visible in TCP/IP packet headers.
- Destination Port: The communication port (usually port 443 for HTTPS) is clearly visible.
- Domain Name (via SNI): As we discussed in the SNI section, the target domain name is sent unencrypted at the start of the TLS handshake. Anyone eavesdropping on the network can tell which website we’re visiting, even though they can’t see which specific page we open inside that website.
- Data Size and Patterns: Even though the data content is encrypted, the total size of data sent and the timing of its transmission remain visible. Advanced traffic analysis techniques can sometimes guess user activity just from these data packet sizes.
[!TIP] If we want to hide the domain name we’re accessing from local network eavesdroppers, we need additional technologies like Encrypted SNI (ESNI) or Encrypted Client Hello (ECH), plus DNS over HTTPS (DoH) to prevent information leaking through DNS queries.
Summary #
- TLS is the current standard: The SSL protocols (v2.0 and v3.0) are no longer secure and are banned. We must make sure our Nginx is configured to support only TLS 1.2 and TLS 1.3.
- Hybrid Connections: HTTPS combines the strengths of asymmetric cryptography for the security of the negotiation (handshake) phase and symmetric cryptography for the speed of the data transfer phase.
- TLS 1.3 is much faster: By cutting the handshake flow to 1-RTT, TLS 1.3 significantly reduces connection latency compared to TLS 1.2, which requires 2-RTT.
- Full Chain is mandatory: Always include the Intermediate CA in the Nginx configuration file so the Chain of Trust verifies completely on all user devices.
- SNI enables Virtual Hosts: The SNI extension sends the target domain name openly at the start of the handshake, allowing Nginx to serve many HTTPS domains with different certificates on a single IP.