Flash cards
Review the key moves
What is the main idea behind Node.js HTTPS Module?
Lesson checks
Practice each idea before moving on
Short Mimo-style checks built from this lesson's code, terms, and sequence.
Which statement best captures the main point of this lesson?
Complete the missing token from the example code.
// ___ require()Put the learning moves in the order that makes the concept easiest to apply.
Introduction to the HTTPS Module
The HTTPS module is a core Node.js module that provides an implementation of the HTTPS protocol, which is essentially HTTP over TLS/SSL.
It's a secure version of the HTTP module, providing encrypted communication between clients and servers.
Why Use HTTPS?
HTTPS is crucial for modern web applications because it:
- Encrypts Data : Protects sensitive information like passwords, credit card numbers, and personal data from eavesdropping
- Authenticates Servers : Verifies that clients are communicating with the intended server
- Ensures Data Integrity : Prevents data from being modified or corrupted during transfer
- Builds Trust : Visual indicators (like the padlock icon) increase user confidence
- Improves SEO : Search engines prioritize HTTPS websites in search results
- Enables Modern Features : Many web APIs (like Geolocation, Service Workers) require HTTPS
How HTTPS Works
- Client initiates a secure connection to the server
- Encrypted session is established using asymmetric encryption
- Symmetric encryption is used for the actual data transfer
Note
Modern HTTPS uses TLS (Transport Layer Security), which is the successor to SSL (Secure Sockets Layer). The terms are often used interchangeably, but SSL is now considered deprecated.
Important: As of 2023, all major browsers require HTTPS for new web features and APIs. Many browsers also mark non-HTTPS sites as "Not Secure."
Importing the Module
To use the HTTPS module in your Node.js application, you can import it using CommonJS or ES modules syntax:
// Using require()
const https = require('https');// Using import (requires "type": "module" in package.json)
import https from 'https';HTTPS vs HTTP API
The HTTPS module has the same interface as the HTTP module, with the main difference being that it creates connections using TLS/SSL.
This means all the methods and events available in the HTTP module are also available in the HTTPS module.
Note
The main difference in usage is that HTTPS requires SSL/TLS certificates, while HTTP does not.
SSL/TLS Certificates
HTTPS requires SSL/TLS certificates to establish secure connections. There are several types of certificates:
Types of Certificates
- Self-Signed Certificates : For development and testing (not trusted by browsers)
- Domain Validated (DV) : Basic validation, just verifies domain ownership
- Organization Validated (OV) : Validates organization details
- Extended Validation (EV) : Highest level of validation, shows company name in browser
- Wildcard Certificates : Secures all subdomains of a domain
Generating Self-Signed Certificates
For development, you can create self-signed certificates using OpenSSL:
# Generate a private key (RSA 2048-bit)
openssl genrsa -out key.pem 2048
openssl req -new -x509 -key key.pem -out cert.pem -days 365 -nodesNote
If there is no key.pem file present, you need to use the " -newkey " option instead of " -key " in the command above.
# Create a config file (san.cnf)
cat > san.cnf << EOF
[req]
distinguished_name = req_distinguished_name
x509_extensions = v3_req
prompt = no
[req_distinguished_name]
C = US
ST = State
L = City
O = Organization
OU = Organizational Unit
CN = localhost
[v3_req]
keyUsage = keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = localhost
IP.1 = 127.0.0.1
EOF
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout key.pem -out cert.pem -config san.cnf -extensions 'v3_req'Only use them for development and testing purposes.
Obtaining Trusted Certificates
- Paid CAs : DigiCert, GlobalSign, Comodo, etc.
- Free CAs : Let's Encrypt, ZeroSSL, Cloudflare
Creating an HTTPS Server
Once you have your SSL/TLS certificates ready, you can create an HTTPS server in Node.js.
The HTTPS server API is very similar to the HTTP server API, with the main difference being the SSL/TLS configuration.
Basic HTTPS Server Example
Here's how to create a basic HTTPS server:
const https = require('https');
const fs = require('fs');
const path = require('path');
const sslOptions = {
key: fs.readFileSync(path.join(__dirname, 'key.pem')),
cert: fs.readFileSync(path.join(__dirname, 'cert.pem')),
// Enable all security features
minVersion: 'TLSv1.2',
// Recommended security settings
secureOptions: require('constants').SSL_OP_NO_SSLv3 |
require('constants').SSL_OP_NO_TLSv1 |
require('constants').SSL_OP_NO_TLSv1_1
};
// Create the HTTPS server
const server = https.createServer(sslOptions, (req, res) => {
// Security headers
res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
res.setHeader('X-Content-Type-Options', 'nosniff');
res.setHeader('X-Frame-Options', 'SAMEORIGIN');
res.setHeader('X-XSS-Protection', '1; mode=block');
res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
// Handle different routes
if (req.url === '/') {
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
res.end('<h1>Welcome to the Secure Server</h1><p>Your connection is encrypted!</p>');
} else if (req.url === '/api/status') {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ status: 'ok', time: new Date().toISOString() }));
} else {
res.writeHead(404, { 'Content-Type': 'text/plain' });
res.end('404 Not Found');
}
});
// Handle server errors server.on('error', (error) => {
console.error('Server error:', error);
});
// Start the server on port 3000 (HTTPS default is 443 but requires root)
const PORT = process.env.PORT || 3000;
server.listen(PORT, '0.0.0.0', () => {
console.log(`Server running at https://localhost:${PORT}`);
console.log('Press Ctrl+C to stop the server');
});Note
On Unix-like systems, ports below 1024 require root privileges. For production, it's common to run Node.js on a high port (like 3000, 8080) and use a reverse proxy like Nginx or Apache to handle SSL termination.
Advanced Server Configuration
For production environments, you might need more advanced SSL/TLS configuration:
const https = require('https');
const fs = require('fs');
const path = require('path');
const tls = require('tls');
// Path to your SSL/TLS files
const sslOptions = {
key: fs.readFileSync(path.join(__dirname, 'privkey.pem')),
cert: fs.readFileSync(path.join(__dirname, 'cert.pem')),
ca: [
fs.readFileSync(path.join(__dirname, 'chain.pem'))
],
// Recommended security settings
minVersion: 'TLSv1.2',
maxVersion: 'TLSv1.3',
ciphers: [
'TLS_AES_256_GCM_SHA384',
'TLS_CHACHA20_POLY1305_SHA256',
'TLS_AES_128_GCM_SHA256',
'ECDHE-ECDSA-AES256-GCM-SHA384',
'ECDHE-RSA-AES256-GCM-SHA384',
'ECDHE-ECDSA-CHACHA20-POLY1305',
'ECDHE-RSA-CHACHA20-POLY1305',
'ECDHE-ECDSA-AES128-GCM-SHA256',
'ECDHE-RSA-AES128-GCM-SHA256'
].join(':'),
honorCipherOrder: true,
// Enable OCSP Stapling
requestCert: true,
rejectUnauthorized: true,
// Enable session resumption
sessionTimeout: 300, // 5 minutes
sessionIdContext: 'my-secure-app',
// Enable HSTS preload
hsts: {
maxAge: 63072000, // 2 years in seconds
includeSubDomains: true,
preload: true
},
// Enable secure renegotiation
secureOptions: require('constants').SSL_OP_LEGACY_SERVER_CONNECT |
require('constants').SSL_OP_NO_SSLv3 |
require('constants').SSL_OP_NO_TLSv1 |
require('constants').SSL_OP_NO_TLSv1_1 |
require('constants').SSL_OP_CIPHER_SERVER_PREFERENCE
};
// Create the HTTPS server
const server = https.createServer(sslOptions, (req, res) => {
// Security headers
const securityHeaders = {
'Strict-Transport-Security': 'max-age=63072000; includeSubDomains; preload',
'X-Content-Type-Options': 'nosniff',
'X-Frame-Options': 'DENY',
'X-XSS-Protection': '1; mode=block',
'Content-Security-Policy': "default-src 'self'",
'Referrer-Policy': 'strict-origin-when-cross-origin',
'Permissions-Policy': 'geolocation=(), microphone=(), camera=()',
};
Object.entries(securityHeaders).forEach(([key, value]) => {
res.setHeader(key, value);
});
// Handle requests
if (req.url === '/') {
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
res.end('<h1>Secure Node.js Server</h1><p>Your connection is secure!</p>');
} else {
res.writeHead(404, { 'Content-Type': 'text/plain' });
res.end('404 Not Found');
}
});
// Handle server errors
server.on('error', (error) => {
console.error('Server error:', error);
});
// Handle uncaught exceptions
process.on('uncaughtException', (error) => {
console.error('Uncaught exception:', error);
// Perform graceful shutdown
server.close(() => process.exit(1));
});
// Handle unhandled promise rejections
process.on('unhandledRejection', (reason, promise) => {
console.error('Unhandled Rejection at:', promise, 'reason:', reason);
});
// Handle graceful shutdown
const gracefulShutdown = () => {
console.log('Shutting down gracefully...');
server.close(() => {
console.log('Server closed');
process.exit(0);
});
// Force close server after 10 seconds
setTimeout(() => {
console.error('Forcing shutdown...');
process.exit(1);
}, 10000);
};
// Listen for shutdown signals
process.on('SIGTERM', gracefulShutdown);
process.on('SIGINT', gracefulShutdown);
// Start the server
const PORT = process.env.PORT || 3000;
const HOST = process.env.HOST || '0.0.0.0';
server.listen(PORT, HOST, () => {
const { address, port } = server.address();
console.log(`Server running at https://${address}:${port}`);
// Output server information
console.log('Node.js version:', process.version);
console.log('Environment:', process.env.NODE_ENV || 'development');
console.log('PID:', process.pid);
});Security Best Practices
- Always use the latest stable version of Node.js for security updates
- Keep your dependencies up to date using
npm auditandnpm update - Use environment variables for sensitive configuration (never commit secrets to version control)
- Implement rate limiting to prevent abuse
- Regularly rotate your SSL/TLS certificates
- Monitor your server for security vulnerabilities
- Use a reverse proxy like Nginx or Apache in production for additional security features
Testing Your HTTPS Server
To test your HTTPS server, you can use curl or a web browser: curl -k https://localhost:3000 curl --cacert /path/to/ca.pem https://yourdomain.com
- Open your web browser and navigate to https://localhost:3000
Making HTTPS Requests
The HTTPS module allows you to make secure HTTP requests to other servers.
This is essential for interacting with secure APIs and web services.
Basic GET Request
Here's how to make a simple GET request to an HTTPS endpoint:
const https = require('https');
const { URL } = require('url');
// Parse the target URL
const apiUrl = new URL('https://api.example.com/data');
// Request options
const options = {
hostname: apiUrl.hostname,
port: 443,
path: apiUrl.pathname + apiUrl.search,
method: 'GET',
headers: {
'User-Agent': 'MySecureApp/1.0',
'Accept': 'application/json',
'Cache-Control': 'no-cache'
},
// Security settings
// Timeout in milliseconds
timeout: 10000, // 10 seconds
};
console.log(`Making request to: https://${options.hostname}${options.path}`);
// Make the HTTPS request
const req = https.request(options, (res) => {
const { statusCode, statusMessage, headers } = res;
const contentType = headers['content-type'] || '';
console.log(`Status: ${statusCode} ${statusMessage}`);
console.log('Headers:', headers);
// Handle redirects
if (statusCode >= 300 && statusCode < 400 && headers.location) {
console.log(`Redirecting to: ${headers.location}`);
// In a real app, you'd handle the redirect
res.resume(); // Discard the response body
return;
}
// Check for successful response
let error;
if (statusCode !== 200) {
error = new Error(`Request Failed.\nStatus Code: ${statusCode}`);
} else if (!/^application\/json/.test(contentType)) {
error = new Error(`Invalid content-type.\nExpected application/json but received ${contentType}`);
}
if (error) {
console.error(error.message);
res.resume(); // Consume response data to free up memory
return;
}
// Process the response
let rawData = '';
res.setEncoding('utf8');
// Collect chunks of data
res.on('data', (chunk) => {
rawData += chunk;
});
// Process the complete response
res.on('end', () => {
try {
const parsedData = JSON.parse(rawData);
console.log('Response data:', parsedData);
} catch (e) {
console.error('Error parsing JSON:', e.message);
}
});
});
// Handle request errors req.on('error', (e) => {
console.error(`Request error: ${e.message}`);
if (e.code === 'ECONNRESET') {
console.error('Connection was reset by the server');
} else if (e.code === 'ETIMEDOUT') {
console.error('Request timed out');
}
});
// Set a timeout for the entire request (including DNS lookup, TCP connect, etc.) req.setTimeout(15000, () => {
req.destroy(new Error('Request timeout after 15 seconds'));
});
// Handle socket errors (network-level errors) req.on('socket', (socket) => {
socket.on('error', (error) => {
console.error('Socket error:', error.message);
req.destroy(error);
});
// Set a timeout for the socket connection
socket.setTimeout(5000, () => {
req.destroy(new Error('Socket timeout after 5 seconds'));
});
});
// End the request (required to send it) req.end();Using https.get() for Simple Requests
For simple GET requests, you can use the more concise https.get() method. This is a convenience method that automatically sets the HTTP method to GET and calls req.end() for you.
const https = require('https');
const { URL } = require('url');
// Parse the URL
const url = new URL('https://jsonplaceholder.typicode.com/posts/1');
// Request options
const options = {
hostname: url.hostname,
path: url.pathname,
method: 'GET',
headers: {
'Accept': 'application/json',
'User-Agent': 'MySecureApp/1.0'
}
};
console.log(`Fetching data from: ${url}`);
// Make the GET request
const req = https.get(options, (res) => {
const { statusCode } = res;
const contentType = res.headers['content-type'];
if (statusCode !== 200) {
console.error(`Request failed with status code: ${statusCode}`);
res.resume(); // Consume response data to free up memory
return;
}
if (!/^application\/json/.test(contentType)) {
console.error(`Expected JSON but got ${contentType}`);
res.resume();
return;
}
let rawData = '';
res.setEncoding('utf8');
// Collect data chunks
res.on('data', (chunk) => {
rawData += chunk;
});
// Process complete response
res.on('end', () => {
try {
const parsedData = JSON.parse(rawData);
console.log('Received data:', parsedData);
} catch (e) {
console.error('Error parsing JSON:', e.message);
}
});
});
// Handle errors
req.on('error', (e) => {
console.error(`Error: ${e.message}`);
});
// Set a timeout
req.setTimeout(10000, () => {
console.error('Request timeout');
req.destroy();
});Making POST Requests
To send data to a server, you can use a POST request.
Here's how to make a secure POST request with JSON data:
const https = require('https');
const { URL } = require('url');
// Request data
const postData = JSON.stringify({
title: 'foo',
body: 'bar',
userId: 1
});
// Parse the URL
const url = new URL('https://jsonplaceholder.typicode.com/posts');
// Request options
const options = {
hostname: url.hostname,
port: 443,
path: url.pathname,
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(postData),
'User-Agent': 'MySecureApp/1.0',
'Accept': 'application/json'
},
timeout: 10000 // 10 seconds
};
console.log('Sending POST request to:', url.toString());
// Create the request
const req = https.request(options, (res) => {
console.log(`Status Code: ${res.statusCode}`);
console.log('Headers:', res.headers);
let responseData = '';
res.setEncoding('utf8');
// Collect response data
res.on('data', (chunk) => {
responseData += chunk;
});
// Process complete response
res.on('end', () => {
try {
const parsedData = JSON.parse(responseData);
console.log('Response:', parsedData);
} catch (e) {
console.error('Error parsing response:', e.message);
}
});
});
// Handle errors
req.on('error', (e) => {
console.error(`Request error: ${e.message}`);
});
// Set a timeout
req.setTimeout(15000, () => {
req.destroy(new Error('Request timeout after 15 seconds'));
});
// Write data to request body
req.write(postData);
// End the request
req.end();Using Promises with HTTPS Requests
To make HTTPS requests more manageable, you can wrap them in a Promise:
const https = require('https');
const { URL } = require('url');
/**
* Makes an HTTPS request and returns a Promise
* @param {Object} options - Request options
* @param {string|Buffer} [data] - Request body (for POST, PUT, etc.)
* @returns {Promise<Object>} - Resolves with response data
*/
function httpsRequest(options, data = null) {
return new Promise((resolve, reject) => {
const req = https.request(options, (res) => {
let responseData = '';
// Collect response data
res.on('data', (chunk) => {
responseData += chunk;
});
// Process complete response
res.on('end', () => {
try {
const contentType = res.headers['content-type'] || '';
const isJSON = /^application\/json/.test(contentType);
const response = {
statusCode: res.statusCode,
headers: res.headers,
data: isJSON ? JSON.parse(responseData) : responseData
};
if (res.statusCode >= 200 && res.statusCode < 300) {
resolve(response);
} else {
const error = new Error(`Request failed with status code ${res.statusCode}`);
error.response = response;
reject(error);
}
} catch (e) {
e.response = { data: responseData };
reject(e);
}
});
});
// Handle errors
req.on('error', (e) => {
reject(e);
});
// Set timeout
req.setTimeout(options.timeout || 10000, () => {
req.destroy(new Error('Request timeout'));
});
// Write data if provided
if (data) {
req.write(data);
}
// End the request
req.end();
});
}
// Example usage
async function fetchData() {
try {
const url = new URL('https://jsonplaceholder.typicode.com/posts/1');
const options = {
hostname: url.hostname,
path: url.pathname,
method: 'GET',
headers: {
'Accept': 'application/json'
},
timeout: 5000
};
const response = await httpsRequest(options);
console.log('Response:', response.data);
} catch (error) {
console.error('Error:', error.message);
if (error.response) {
console.error('Response data:', error.response.data);
}
}
}
// Run the example
fetchData();Best Practices for HTTPS Requests
- Always validate and sanitize input data before sending it in a request
- Use environment variables for sensitive information like API keys
- Implement proper error handling and timeouts
- Set appropriate headers (Content-Type, Accept, User-Agent)
- Handle redirects appropriately (3xx status codes)
- Implement retry logic for transient failures
- Consider using a library like axios or node-fetch for more complex scenarios
HTTPS Server with Express.js
While you can use the core HTTPS module directly, most Node.js applications use a web framework like Express.js to handle HTTP/HTTPS requests.
Here's how to set up an Express application with HTTPS support.
Basic Express.js HTTPS Server
const express = require('express');
const https = require('https');
const fs = require('fs');
const path = require('path');
const helmet = require('helmet'); // Security middleware
// Create Express app
const app = express();
// Security middleware
app.use(helmet());
// Parse JSON and URL-encoded bodies
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// Serve static files from 'public' directory
app.use(express.static(path.join(__dirname, 'public'), {
dotfiles: 'ignore',
etag: true,
extensions: ['html', 'htm'],
index: 'index.html',
maxAge: '1d',
redirect: true
}));
// Routes
app.get('/', (req, res) => {
res.send('<h1>Welcome to Secure Express Server</h1>');
});
app.get('/api/status', (req, res) => {
res.json({
status: 'operational',
timestamp: new Date().toISOString(),
environment: process.env.NODE_ENV || 'development',
nodeVersion: process.version
});
});
// Error handling middleware
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({ error: 'Something went wrong!' });
});
// 404 handler
app.use((req, res) => {
res.status(404).json({ error: 'Not Found' });
});
// SSL/TLS options
const sslOptions = {
key: fs.readFileSync(path.join(__dirname, 'key.pem')),
cert: fs.readFileSync(path.join(__dirname, 'cert.pem')),
// Enable HTTP/2 if available
allowHTTP1: true,
// Recommended security options
minVersion: 'TLSv1.2',
ciphers: [
'TLS_AES_256_GCM_SHA384',
'TLS_CHACHA20_POLY1305_SHA256',
'TLS_AES_128_GCM_SHA256',
'ECDHE-RSA-AES128-GCM-SHA256',
'!DSS',
'!aNULL',
'!eNULL',
'!EXPORT',
'!DES',
'!RC4',
'!3DES',
'!MD5',
'!PSK'
].join(':'),
honorCipherOrder: true
};
// Create HTTPS server
const PORT = process.env.PORT || 3000;
const server = https.createServer(sslOptions, app);
// Handle unhandled promise rejections
process.on('unhandledRejection', (reason, promise) => {
console.error('Unhandled Rejection at:', promise, 'reason:', reason);
});
// Handle uncaught exceptions
process.on('uncaughtException', (error) => {
console.error('Uncaught Exception:', error);
// Perform cleanup and exit if needed
process.exit(1);
});
// Graceful shutdown
const gracefulShutdown = (signal) => {
console.log(`\nReceived ${signal}. Shutting down gracefully...`);
server.close(() => {
console.log('HTTP server closed.');
// Close database connections, etc.
process.exit(0);
});
// Force close server after 10 seconds
setTimeout(() => {
console.error('Forcing shutdown...');
process.exit(1);
}, 10000);
};
// Listen for shutdown signals
process.on('SIGTERM', gracefulShutdown);
process.on('SIGINT', gracefulShutdown);
// Start the server
const HOST = process.env.HOST || '0.0.0.0';
server.listen(PORT, HOST, () => {
console.log(`Express server running at https://${HOST}:${PORT}`);
console.log('Environment:', process.env.NODE_ENV || 'development');
console.log('Press Ctrl+C to stop the server');
});Using Environment Variables
It's a best practice to use environment variables for configuration. Create a .env file:
NODE_ENV=development
PORT=3000
HOST=0.0.0.0
SSL_KEY_PATH=./key.pem
SSL_CERT_PATH=./cert.pemThen use the dotenv package to load them:
require('dotenv').config();
// Access environment variables
const PORT = process.env.PORT || 3000;
const HOST = process.env.HOST || '0.0.0.0';
const sslOptions = {
key: fs.readFileSync(process.env.SSL_KEY_PATH),
cert: fs.readFileSync(process.env.SSL_CERT_PATH)
// ... other options
};Production Deployment
In production, it's recommended to use a reverse proxy like Nginx or Apache in front of your Node.js application. This provides:
- SSL/TLS termination
- Load balancing
- Static file serving
- Request caching
- Rate limiting
- Better security headers
server {
listen 443 ssl http2;
server_name yourdomain.com;
# SSL configuration
ssl_certificate /path/to/your/cert.pem;
ssl_certificate_key /path/to/your/key.pem;
# Security headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
# Proxy to Node.js app
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# Serve static files directly
location /static/ {
root /path/to/your/app/public;
expires 30d;
access_log off;
}
}
# Redirect HTTP to HTTPS
server {
listen 80;
server_name yourdomain.com;
return 301 https://$host$request_uri;
}
# Redirect HTTP to HTTPS
server {
listen 80;
server_name yourdomain.com;
return 301 https://$host$request_uri;
}Best Practices for Express.js with HTTPS:
- Always use helmet middleware for security headers
- Set secure session options (if using sessions)
- Use environment variables for configuration
- Implement proper error handling and logging
- Use a reverse proxy in production
- Keep your dependencies up to date
- Use HTTP/2 for better performance
- Implement rate limiting to prevent abuse
- Use CORS middleware if your API is accessed from different domains
HTTP/2 with Node.js
HTTP/2 is a major revision of the HTTP protocol that provides significant performance improvements over HTTP/1.1. When combined with HTTPS, it offers both security and performance benefits for modern web applications.
Benefits of HTTP/2
Key Features of HTTP/2
- Multiplexing : Multiple requests/responses can be sent in parallel over a single connection, eliminating head-of-line blocking
- Header Compression : Reduces overhead by compressing HTTP headers (HPACK algorithm)
- Server Push : Server can proactively send resources to the client before they're requested
- Binary Protocol : More efficient to parse than HTTP/1.1's text-based format
- Stream Prioritization : More important resources can be loaded first
- Connection Multiplexing : Multiple streams can share a single TCP connection