HTTPS & Transport Security
HTTPS encrypts data in transit between client and server, preventing eavesdropping and tampering. It's no longer optional—it's required for any modern web application.
Why HTTPS Matters
- Encryption - Data can't be read by attackers
- Integrity - Data can't be modified in transit
- Authentication - Users know they're on the real site
- SEO - Google ranks HTTPS sites higher
- Features - Required for HTTP/2, service workers, etc.
Setting Up HTTPS with Let's Encrypt
# Using Certbot for automatic certificate management
sudo apt install certbot python3-certbot-nginx
# Get certificate
sudo certbot --nginx -d example.com -d www.example.com
# Auto-renewal is set up automatically
# Test with:
sudo certbot renew --dry-run
Node.js HTTPS Server
const https = require('https');
const fs = require('fs');
const express = require('express');
const app = express();
const options = {
key: fs.readFileSync('/path/to/privkey.pem'),
cert: fs.readFileSync('/path/to/fullchain.pem'),
// Modern TLS configuration
minVersion: 'TLSv1.2',
ciphers: [
'ECDHE-ECDSA-AES128-GCM-SHA256',
'ECDHE-RSA-AES128-GCM-SHA256',
'ECDHE-ECDSA-AES256-GCM-SHA384',
'ECDHE-RSA-AES256-GCM-SHA384',
].join(':'),
};
https.createServer(options, app).listen(443);
// Redirect HTTP to HTTPS
const http = require('http');
http.createServer((req, res) => {
res.writeHead(301, { Location: `https://${req.headers.host}${req.url}` });
res.end();
}).listen(80);
HSTS (HTTP Strict Transport Security)
// Force HTTPS for all future requests
app.use((req, res, next) => {
res.setHeader(
'Strict-Transport-Security',
'max-age=31536000; includeSubDomains; preload'
);
next();
});
// Or with Helmet
const helmet = require('helmet');
app.use(helmet.hsts({
maxAge: 31536000, // 1 year
includeSubDomains: true, // Apply to all subdomains
preload: true, // Allow HSTS preloading
}));
Next.js HTTPS Configuration
// next.config.js
module.exports = {
async headers() {
return [
{
source: '/:path*',
headers: [
{
key: 'Strict-Transport-Security',
value: 'max-age=31536000; includeSubDomains; preload',
},
],
},
];
},
// Force HTTPS in production
async redirects() {
return process.env.NODE_ENV === 'production'
? [
{
source: '/:path*',
has: [{ type: 'header', key: 'x-forwarded-proto', value: 'http' }],
destination: 'https://example.com/:path*',
permanent: true,
},
]
: [];
},
};
Certificate Pinning
// Public Key Pinning (for mobile apps or critical applications)
const https = require('https');
const crypto = require('crypto');
const expectedFingerprint = 'SHA256:XXXX...';
const options = {
hostname: 'api.example.com',
port: 443,
checkServerIdentity: (host, cert) => {
const fingerprint = crypto
.createHash('sha256')
.update(cert.raw)
.digest('base64');
if (`SHA256:${fingerprint}` !== expectedFingerprint) {
throw new Error('Certificate fingerprint mismatch!');
}
},
};
https.get(options, (res) => {
// Proceed with request
});
Nginx HTTPS Configuration
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name example.com www.example.com;
# Certificate files
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# Modern TLS configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
# HSTS
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
# OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
# Session configuration
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;
}
Testing TLS Configuration
# Test with SSL Labs
# Visit: https://www.ssllabs.com/ssltest/
# Test with OpenSSL
openssl s_client -connect example.com:443 -servername example.com
# Check certificate expiration
echo | openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -dates
# Test specific TLS version
openssl s_client -connect example.com:443 -tls1_2
openssl s_client -connect example.com:443 -tls1_3
Mixed Content Issues
<!-- BAD: Loading HTTP resources on HTTPS page -->
<img src="http://example.com/image.jpg">
<script src="http://cdn.example.com/script.js"></script>
<!-- GOOD: Use HTTPS or protocol-relative URLs -->
<img src="https://example.com/image.jpg">
<img src="//example.com/image.jpg">
<!-- Best: Use CSP to block mixed content -->
<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">
HTTPS Checklist
- Use TLS 1.2 or 1.3 only (disable older versions)
- Use strong cipher suites
- Enable HSTS with long max-age
- Redirect all HTTP to HTTPS
- Use secure cookies (Secure flag)
- Fix mixed content issues
- Set up automatic certificate renewal
- Enable OCSP stapling
- Test with SSL Labs (aim for A+ rating)