HTTPS / TLS — encrypting data in transit; what the handshake does
Concept
HTTPS/TLS — HTTPS is HTTP over TLS (Transport Layer Security). TLS encrypts the communication between the client (browser) and the server so that eavesdroppers cannot read or tamper with the data.
TLS (Transport Layer Security): The cryptographic protocol. Replaced the older SSL (Secure Sockets Layer) — "SSL" is now a common but technically incorrect term for TLS.
What TLS provides:
- Encryption: Data is encrypted in transit. Interceptors see ciphertext, not plaintext.
- Authentication: The server proves its identity via a certificate signed by a trusted Certificate Authority (CA). Prevents impersonation.
- Integrity: Data cannot be tampered with in transit (HMAC-based integrity check).
TLS Handshake (simplified):
- Client says "Hello" and lists supported ciphers.
- Server responds with its certificate and chosen cipher.
- Client verifies the certificate (checks CA signature, expiry, domain match).
- Client and server exchange keys and derive a shared session key.
- All subsequent communication is encrypted with the session key.
Certificates: Issued by Certificate Authorities (CAs) like Let's Encrypt (free), Comodo, DigiCert. The browser trusts a built-in list of CAs.
Let's Encrypt: Free, automated, 90-day certificates. Certbot automates renewal. Caddy web server auto-provisions Let's Encrypt certificates.
HSTS (HTTP Strict Transport Security): HTTP header that tells browsers to ONLY use HTTPS for a domain, even if the user types http://. Prevents downgrade attacks.
Mixed content: An HTTPS page loading HTTP resources. Browsers block this. All assets (images, JS, CSS) must also be over HTTPS.
Code Example
# Nginx HTTPS configuration with TLS
server {
listen 443 ssl http2;
server_name example.com www.example.com;
# Certificate files (from Let's Encrypt / Certbot)
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# Modern TLS configuration (disable old, insecure protocols)
ssl_protocols TLSv1.2 TLSv1.3; # TLS 1.0 and 1.1 are deprecated
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers off;
# HSTS — force HTTPS for 1 year
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# ... rest of config
}
# HTTP → HTTPS redirect
server {
listen 80;
server_name example.com www.example.com;
return 301 https://example.com$request_uri; # permanent redirect to HTTPS
}# Let's Encrypt with Certbot
certbot --nginx -d example.com -d www.example.com
# Auto-modifies nginx config, sets up auto-renewal via cron
# Caddy (auto-HTTPS with no configuration)
# Caddyfile:
# example.com {
# root * /var/www/public
# php_fastcgi unix//var/run/php/php8.4-fpm.sock
# file_server
# }
# Caddy automatically obtains and renews Let's Encrypt certs!<?php
// Laravel — force HTTPS in production
// app/Providers/AppServiceProvider.php
public function boot(): void
{
if (app()->isProduction()) {
URL::forceScheme('https'); // all generated URLs use https://
\Illuminate\Support\Facades\URL::forceRootUrl(config('app.url'));
}
}
// Or via middleware:
// app/Http/Middleware/ForceHttps.php
public function handle(Request $request, Closure $next): Response
{
if (!$request->secure() && app()->isProduction()) {
return redirect()->secure($request->getRequestUri());
}
return $next($request);
}