Menu
×
   ❮     
HTML CSS JAVASCRIPT SQL PYTHON JAVA PHP HOW TO W3.CSS C C++ C# BOOTSTRAP REACT MYSQL JQUERY EXCEL XML DJANGO NUMPY PANDAS NODEJS DSA TYPESCRIPT ANGULAR GIT POSTGRESQL MONGODB ASP AI R GO KOTLIN SASS VUE GEN AI SCIPY CYBERSECURITY DATA SCIENCE INTRO TO PROGRAMMING BASH RUST

Node.js Tutorial

Node HOME Node Intro Node Get Started Node JS Requirements Node.js vs Browser Node Cmd Line Node V8 Engine Node Architecture Node Event Loop

Asynchronous

Node Async Node Promises Node Async/Await Node Errors Handling

Module Basics

Node Modules Node ES Modules Node NPM Node package.json Node NPM Scripts Node Manage Dep Node Publish Packages

Core Modules

HTTP Module HTTPS Module File System (fs) Path Module OS Module URL Module Events Module Stream Module Buffer Module Crypto Module Timers Module DNS Module Assert Module Util Module Readline Module

JS & TS Features

Node ES6+ Node Process Node TypeScript Node Adv. TypeScript Node Lint & Formatting

Building Applications

Node Frameworks Express.js Middleware Concept REST API Design API Authentication Node.js with Frontend

Database Integration

MySQL Get Started MySQL Create Database MySQL Create Table MySQL Insert Into MySQL Select From MySQL Where MySQL Order By MySQL Delete MySQL Drop Table MySQL Update MySQL Limit MySQL Join
MongoDB Get Started MongoDB Create DB MongoDB Collection MongoDB Insert MongoDB Find MongoDB Query MongoDB Sort MongoDB Delete MongoDB Drop Collection MongoDB Update MongoDB Limit MongoDB Join

Advanced Communication

GraphQL Socket.IO WebSockets

Testing & Debugging

Node Adv. Debugging Node Testing Apps Node Test Frameworks Node Test Runner

Node.js Deployment

Node Env Variables Node Dev vs Prod Node CI/CD Node Security Node Deployment

Perfomance & Scaling

Node Logging Node Monitoring Node Performance Child Process Module Cluster Module Worker Threads

Node.js Advanced

Microservices Node WebAssembly HTTP2 Module Perf_hooks Module VM Module TLS/SSL Module Net Module Zlib Module Real-World Examples

Hardware & IoT

RasPi Get Started RasPi GPIO Introduction RasPi Blinking LED RasPi LED & Pushbutton RasPi Flowing LEDs RasPi WebSocket RasPi RGB LED WebSocket RasPi Components

Node.js Reference

Built-in Modules EventEmitter (events) Worker (cluster) Cipher (crypto) Decipher (crypto) DiffieHellman (crypto) ECDH (crypto) Hash (crypto) Hmac (crypto) Sign (crypto) Verify (crypto) Socket (dgram, net, tls) ReadStream (fs, stream) WriteStream (fs, stream) Server (http, https, net, tls) Agent (http, https) Request (http) Response (http) Message (http) Interface (readline)

Resources & Tools

Node.js Compiler Node.js Server Node.js Quiz Node.js Exercises Node.js Syllabus Node.js Study Plan Node.js Certificate

Node.js HTTPS Module


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

  1. Client initiates a secure connection to the server
  2. Server presents its SSL/TLS certificate to the client
  3. Client verifies the certificate with a trusted Certificate Authority (CA)
  4. Encrypted session is established using asymmetric encryption
  5. 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."


Getting Started with HTTPS

Importing the Module

To use the HTTPS module in your Node.js application, you can import it using CommonJS or ES modules syntax:

CommonJS (Node.js default)

// Using require()
const https = require('https');

ES Modules (Node.js 14+)

// 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
  • Multi-Domain (SAN) Certificates: Secures multiple domains with one certificate

Generating Self-Signed Certificates

For development, you can create self-signed certificates using OpenSSL:

Basic Self-Signed Certificate

# Generate a private key (RSA 2048-bit)
openssl genrsa -out key.pem 2048

# Generate a self-signed certificate (valid for 365 days)
openssl req -new -x509 -key key.pem -out cert.pem -days 365 -nodes

Note: If there is no key.pem file present, you need to use the "-newkey" option instead of "-key" in the command above.

With Subject Alternative Names (SAN)

# 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

# Generate key and certificate with SAN
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout key.pem -out cert.pem -config san.cnf -extensions 'v3_req'

Security Note: Self-signed certificates will trigger security warnings in browsers because they're not signed by a trusted Certificate Authority.

Only use them for development and testing purposes.

Obtaining Trusted Certificates

For production, obtain certificates from trusted Certificate Authorities (CAs):

  • Paid CAs: DigiCert, GlobalSign, Comodo, etc.
  • Free CAs: Let's Encrypt, ZeroSSL, Cloudflare

Let's Encrypt is a popular free, automated, and open Certificate Authority that provides trusted certificates.


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:

Basic Secure Server

const https = require('https');
const fs = require('fs');
const path = require('path');

// Path to your SSL/TLS certificate and key
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:

Advanced HTTPS Server with OCSP Stapling and Session Resumption

const https = require('https');
const fs = require('fs');
const path = require('path');
const tls = require('tls');

// Path to your SSL/TLS files
const sslOptions = {
  // Certificate and key
  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 audit` and `npm 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:

Using curl

# Skip certificate verification (for self-signed certs)
curl -k https://localhost:3000

# With certificate verification (for trusted certs)
curl --cacert /path/to/ca.pem https://yourdomain.com

Using a Web Browser

  1. Open your web browser and navigate to https://localhost:3000
  2. If using a self-signed certificate, you'll need to accept the security warning
  3. For development, you can add your self-signed certificate to your trusted root certificates


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:

Basic HTTPS GET Request

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
  rejectUnauthorized: true, // Verify the server certificate (default: true)
  // 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.

Simple GET Request with https.get()

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:

HTTPS POST Request with JSON

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:

Promise-based HTTPS Request

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

Express with HTTPS

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:

.env file

NODE_ENV=development PORT=3000
HOST=0.0.0.0
SSL_KEY_PATH=./key.pem
SSL_CERT_PATH=./cert.pem

Then use the dotenv package to load them:

Loading Environment Variables

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

Example Nginx Configuration

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

HTTP/2 Server Example

Basic HTTP/2 Server

const http2 = require('http2');
const fs = require('fs');
const path = require('path');

// SSL/TLS options
const serverOptions = {
  key: fs.readFileSync(path.join(__dirname, 'key.pem')),
  cert: fs.readFileSync(path.join(__dirname, 'cert.pem')),
  allowHTTP1: true, // Fallback to HTTP/1.1 if needed

  // Recommended security settings
  minVersion: 'TLSv1.2',
  ciphers: [
    'TLS_AES_256_GCM_SHA384',
    'TLS_CHACHA20_POLY1305_SHA256',
    'TLS_AES_128_GCM_SHA256',
    'ECDHE-ECDSA-AES256-GCM-SHA384',
    '!aNULL',
    '!eNULL',
    '!EXPORT',
    '!DES',
    '!RC4',
    '!3DES',
    '!MD5',
    '!PSK'
  ].join(':'),
  honorCipherOrder: true
};

// Create HTTP/2 server
const server = http2.createSecureServer(serverOptions);

// Handle incoming requests
server.on('stream', (stream, headers) => {
  const method = headers[':method'];
  const path = headers[':path'];
  const scheme = headers[':scheme'];
  const authority = headers[':authority'];

  console.log(`${method} ${path} (HTTP/2)`);

  // Handle different routes
  if (path === '/') {
  // Set response headers
    stream.respond({
      'content-type': 'text/html; charset=utf-8',
      ':status': 200,
      'x-powered-by': 'Node.js HTTP/2',
      'cache-control': 'public, max-age=3600'
    });

    // Send HTML response
    stream.end(`
      <!DOCTYPE html>
      <html>
      <head>
      <title>HTTP/2 Server</title>
      <link rel="stylesheet" href="/styles.css">
      </head>
      <body>
        <h1>Hello from HTTP/2 Server!</h1>
        <p>This page is served over HTTP/2.</p>
        <div id="data">Loading data...</div>
        <script src="/app.js"></script>
      </body>
      </html>
      `);
    }
    // API endpoint
    else if (path === '/api/data' && method === 'GET') {
      stream.respond({
        'content-type': 'application/json',
        ':status': 200,
        'cache-control': 'no-cache'
      });

      stream.end(JSON.stringify({
        message: 'Data from HTTP/2 API',
        timestamp: new Date().toISOString(),
        protocol: 'HTTP/2',
        server: 'Node.js HTTP/2 Server'
      }));
    }
    // Server Push example
    else if (path === '/push') {
      // Push additional resources
      stream.pushStream({ ':path': '/styles.css' }, (err, pushStream) => {
        if (err) {
          console.error('Push stream error:', err);
          return;
        }
        pushStream.respond({
          'content-type': 'text/css',
          ':status': 200
        });
        pushStream.end('body { font-family: Arial, sans-serif; margin: 2em; }');
      }
      stream.respond({
        'content-type': 'text/html; charset=utf-8',
        ':status': 200
      });
      stream.end('<h1>Server Push Example</h1><link rel="stylesheet" href="/styles.css">');
    }
    // 404 Not Found
  else {
    stream.respond({
      'content-type': 'text/plain',
      ':status': 404
    });
    stream.end('404 - Not Found');
  }
});

// Handle errors
server.on('error', (err) => {
  console.error('Server error:', err);
  process.exit(1);
});

// Start the server
const PORT = process.env.PORT || 8443;
server.listen(PORT, '0.0.0.0', () => {
  console.log(`HTTP/2 server running at https://localhost:${PORT}`);
  console.log('Environment:', process.env.NODE_ENV || 'development');
  console.log('Press Ctrl+C to stop the server');
});

// Graceful shutdown
const gracefulShutdown = (signal) => {
  console.log(`\nReceived ${signal}. Shutting down gracefully...`);
  server.close(() => {
    console.log('HTTP/2 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);

HTTP/2 with Express.js

To use HTTP/2 with Express.js, you can use the spdy package, which provides HTTP/2 support for Express applications:

Express.js with HTTP/2

npm install spdy --save
const express = require('express');
const spdy = require('spdy');
const fs = require('fs');
const path = require('path');

const app = express();

// Your Express middleware and routes here
app.get('/', (req, res) => {
  res.send('Hello from Express over HTTP/2!');
});

// SSL/TLS options
const options = {
  key: fs.readFileSync(path.join(__dirname, 'key.pem')),
  cert: fs.readFileSync(path.join(__dirname, 'cert.pem')),
  spdy: {
    protocols: ['h2', 'http/1.1'], // Allow both HTTP/2 and HTTP/1.1
    plain: false, // Use TLS
    'x-forwarded-for': true
  }
};

// Create HTTP/2 server with Express
const PORT = process.env.PORT || 3000;
spdy.createServer(options, app).listen(PORT, () => {
  console.log(`Express server with HTTP/2 running on port ${PORT}`);
});

Testing HTTP/2 Support

You can verify that your server is using HTTP/2 with these methods:

Using cURL

# Check if server supports HTTP/2
curl -I --http2 https://localhost:8443

# Force HTTP/2 with verbose output
curl -v --http2 https://localhost:8443

# Test with HTTP/2 prior knowledge (no upgrade)
curl --http2-prior-knowledge -I https://localhost:8443

Using Chrome DevTools

  1. Open Chrome DevTools (F12 or right-click → Inspect)
  2. Go to the Network tab
  3. Right-click on the column headers and enable "Protocol"
  4. Look for "h2" in the Protocol column for HTTP/2 requests
  5. Click on a request to see detailed protocol information

Note: HTTP/2 requires HTTPS in browsers, though the protocol itself doesn't require encryption. All major browsers only support HTTP/2 over TLS (HTTPS).

Important: When using HTTP/2, ensure your SSL/TLS configuration is up to date and follows security best practices, as many HTTP/2 features rely on a secure connection.


Comparing HTTP and HTTPS

Feature HTTP HTTPS
Data Encryption No (plain text) Yes (encrypted)
Server Authentication No Yes (via certificates)
Data Integrity No protection Protected (tampering detected)
Default Port 80 443
Performance Faster Slight overhead (but optimized with HTTP/2)
SEO Ranking Lower Higher (Google prefers HTTPS)
Setup Complexity Simpler More complex (requires certificates)

Summary and Best Practices

In this comprehensive guide, we've explored the Node.js HTTPS module and its capabilities for creating secure web applications. Here's a summary of the key points and best practices:

Key Takeaways

  • HTTPS is Essential: Modern web development requires HTTPS to ensure data security, user privacy, and compliance with web standards.
  • Certificate Management: Properly manage SSL/TLS certificates, whether using self-signed certificates for development or trusted certificates from CAs for production.
  • Security First: Always implement security best practices, including proper TLS configuration, secure headers, and input validation.
  • Performance Matters: Leverage HTTP/2 for improved performance through features like multiplexing, header compression, and server push.
  • Production Readiness: Use reverse proxies (like Nginx) in production for better security, performance, and reliability.

Security Checklist

Before deploying your HTTPS-enabled application to production, verify:

  • Use TLS 1.2 or higher (1.3 recommended)
  • Implement HSTS (HTTP Strict Transport Security)
  • Use secure cipher suites and disable weak ones
  • Keep your Node.js and dependencies updated
  • Implement proper error handling and logging
  • Set secure cookie flags (Secure, HttpOnly, SameSite)
  • Use Content Security Policy (CSP) headers
  • Implement rate limiting and request validation

Performance Optimization

  • Enable HTTP/2 for better performance
  • Implement session resumption to reduce TLS handshake overhead
  • Use OCSP stapling to improve TLS handshake performance
  • Optimize your certificate chain (keep it short and complete)
  • Enable session tickets for better performance with session resumption

Remember that security is an ongoing process. Regularly audit your application, keep dependencies updated, and stay informed about the latest security best practices and vulnerabilities.




×

Contact Sales

If you want to use W3Schools services as an educational institution, team or enterprise, send us an e-mail:
sales@w3schools.com

Report Error

If you want to report an error, or if you want to make a suggestion, send us an e-mail:
help@w3schools.com

W3Schools is optimized for learning and training. Examples might be simplified to improve reading and learning. Tutorials, references, and examples are constantly reviewed to avoid errors, but we cannot warrant full correctness of all content. While using W3Schools, you agree to have read and accepted our terms of use, cookie and privacy policy.

Copyright 1999-2025 by Refsnes Data. All Rights Reserved. W3Schools is Powered by W3.CSS.