All Deployment guides Deployment

How to Use SSL Certificates with Node.js

Node.js has built-in HTTPS support through the https module. You load your certificate files and create an HTTPS server. This guide covers raw Node.js, Express, and the recommended production setup.

Basic HTTPS server

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

const options = {
  key: fs.readFileSync('/etc/ssl/privkey.pem'),
  cert: fs.readFileSync('/etc/ssl/fullchain.pem'),
};

https.createServer(options, (req, res) => {
  res.writeHead(200);
  res.end('Hello HTTPS');
}).listen(443);

Express with HTTPS

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

const app = express();

app.get('/', (req, res) => {
  res.send('Hello HTTPS');
});

const options = {
  key: fs.readFileSync('/etc/ssl/privkey.pem'),
  cert: fs.readFileSync('/etc/ssl/fullchain.pem'),
};

https.createServer(options, app).listen(443, () => {
  console.log('HTTPS server running on port 443');
});

Redirect HTTP to HTTPS

Run both an HTTP and HTTPS server:

const http = require('http');
const https = require('https');
const fs = require('fs');
const express = require('express');

const app = express();
// ... your routes

const options = {
  key: fs.readFileSync('/etc/ssl/privkey.pem'),
  cert: fs.readFileSync('/etc/ssl/fullchain.pem'),
};

// HTTPS server
https.createServer(options, app).listen(443);

// HTTP redirect
http.createServer((req, res) => {
  res.writeHead(301, { Location: `https://${req.headers.host}${req.url}` });
  res.end();
}).listen(80);

Production recommendation: reverse proxy

For production, don’t terminate TLS in Node.js directly. Use a reverse proxy (Nginx, Caddy, or a load balancer) in front:

Client ──HTTPS──→ Nginx (TLS termination) ──HTTP──→ Node.js (port 3000)

Reasons:

  • Nginx handles TLS more efficiently (C vs JavaScript)
  • Certificate renewal doesn’t require Node.js restart
  • Port 443 requires root — Node.js shouldn’t run as root
  • Rate limiting, caching, compression handled by the proxy
  • HTTP/2 support is more mature in Nginx

Node.js direct HTTPS is fine for development, internal services, or small projects.

Common frameworks

Fastify

const fastify = require('fastify')({
  https: {
    key: fs.readFileSync('/etc/ssl/privkey.pem'),
    cert: fs.readFileSync('/etc/ssl/fullchain.pem'),
  },
});

fastify.get('/', async () => ({ hello: 'https' }));
fastify.listen({ port: 443, host: '0.0.0.0' });

Koa

const Koa = require('koa');
const https = require('https');
const fs = require('fs');

const app = new Koa();
app.use(ctx => { ctx.body = 'Hello HTTPS'; });

https.createServer({
  key: fs.readFileSync('/etc/ssl/privkey.pem'),
  cert: fs.readFileSync('/etc/ssl/fullchain.pem'),
}, app.callback()).listen(443);

Next.js / Nuxt / other framework dev servers

Framework dev servers usually have a --https flag for local development. For production, always use a reverse proxy — these frameworks serve static files or run SSR behind Nginx, Caddy, or a cloud load balancer.

Environment variable pattern

Don’t hardcode certificate paths. Use environment variables so the same code works in dev and production:

const options = process.env.SSL_KEY ? {
  key: fs.readFileSync(process.env.SSL_KEY),
  cert: fs.readFileSync(process.env.SSL_CERT),
} : null;

if (options) {
  https.createServer(options, app).listen(443);
  console.log('HTTPS server on port 443');
} else {
  app.listen(3000);
  console.log('HTTP server on port 3000 (no SSL_KEY set)');
}

Development with self-signed certificates

For local development, generate a self-signed certificate (not for production):

openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:P-256 \
  -keyout dev-key.pem -out dev-cert.pem -days 365 -nodes \
  -subj "/CN=localhost"
const options = {
  key: fs.readFileSync('./dev-key.pem'),
  cert: fs.readFileSync('./dev-cert.pem'),
};

Your browser will show a warning (self-signed certificates aren’t trusted) — click through for development.

Hot-reloading certificates

To reload certificates without restarting (useful for Let’s Encrypt renewal):

const tls = require('tls');

function loadCerts() {
  return {
    key: fs.readFileSync('/etc/ssl/privkey.pem'),
    cert: fs.readFileSync('/etc/ssl/fullchain.pem'),
  };
}

const server = https.createServer({
  SNICallback: (hostname, cb) => {
    const ctx = tls.createSecureContext(loadCerts());
    cb(null, ctx);
  },
}, app);

This re-reads the files on each new connection. For better performance, add a file watcher that reloads only when files change.

Frequently asked questions

Should I handle SSL in Node.js or use a reverse proxy?

For production: use a reverse proxy. For development, small projects, or internal services: Node.js direct HTTPS is fine. The reverse proxy pattern is standard because it separates concerns and handles TLS more efficiently.

Can I use GetHTTPS certificates with Node.js?

Yes. GetHTTPS produces standard PEM files (privkey.pem, fullchain.pem) that Node.js reads directly with fs.readFileSync().

How do I handle certificate renewal in Node.js?

If using a reverse proxy, just replace the files and reload the proxy — Node.js doesn’t need to restart. If handling TLS directly, use the SNICallback approach above or restart the Node.js process after replacing the certificate files.

What about localhost SSL for development?

Use a self-signed certificate (shown above) or mkcert for a locally-trusted development certificate. Don’t use Let’s Encrypt for localhost — it can’t validate a domain that isn’t publicly accessible.

Can I use GetHTTPS certificates with Deno or Bun?

Yes. Both Deno and Bun support TLS with PEM files:

Deno:

Deno.serve({
  port: 443,
  cert: Deno.readTextFileSync("/etc/ssl/fullchain.pem"),
  key: Deno.readTextFileSync("/etc/ssl/privkey.pem"),
}, (req) => new Response("Hello HTTPS"));

Bun:

Bun.serve({
  port: 443,
  tls: {
    cert: Bun.file("/etc/ssl/fullchain.pem"),
    key: Bun.file("/etc/ssl/privkey.pem"),
  },
  fetch(req) { return new Response("Hello HTTPS"); },
});

The same PEM files from GetHTTPS work across all JavaScript runtimes.

Related articles

Getting Started 2026-05-08
How to Get a Free SSL Certificate (Step-by-Step Guide)
Get a free SSL certificate from Let's Encrypt in 5 minutes — no software to install, no account to create. Complete guide covering 4 methods, both challenge types, installation on 6 platforms, and troubleshooting.
Deployment 2026-05-08
SSL Certificates with Docker and Reverse Proxies
Configure HTTPS for Docker containers using Nginx reverse proxy, Traefik with automatic Let's Encrypt, or manual certificate mounting.
SSL & Certificates 2026-05-07
SSL Certificate Formats: PEM, PFX, DER Explained
Understand PEM, PFX/PKCS#12, and DER certificate formats. Learn which format your server needs and how to convert between them with OpenSSL.
Get a free SSL certificate in your browser
No installation, no account. Your private key never leaves your device.
Get your certificate