Todas las guías de despliegue Despliegue

Cómo usar certificados SSL con Node.js

Node.js tiene soporte HTTPS integrado a través del módulo https. Cargas tus archivos de certificado y creas un servidor HTTPS. Esta guía cubre Node.js puro, Express y la configuración recomendada para producción.

Servidor HTTPS básico

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 con 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');
});

Redirigir HTTP a HTTPS

Ejecuta tanto un servidor HTTP como HTTPS:

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);

Recomendación para producción: proxy inverso

Para producción, no termines TLS directamente en Node.js. Usa un proxy inverso (Nginx, Caddy o un balanceador de carga) delante:

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

Razones:

  • Nginx maneja TLS más eficientemente (C vs JavaScript)
  • La renovación de certificados no requiere reiniciar Node.js
  • El puerto 443 requiere root — Node.js no debería ejecutarse como root
  • Limitación de tasa, caché, compresión manejados por el proxy
  • HTTP/2 tiene soporte más maduro en Nginx

Node.js con HTTPS directo está bien para desarrollo, servicios internos o proyectos pequeños.

Frameworks comunes

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 / otros servidores de desarrollo de frameworks

Los servidores de desarrollo de frameworks generalmente tienen un flag --https para desarrollo local. Para producción, siempre usa un proxy inverso: estos frameworks sirven archivos estáticos o ejecutan SSR detrás de Nginx, Caddy o un balanceador de carga en la nube.

Patrón con variables de entorno

No codifiques las rutas de los certificados directamente. Usa variables de entorno para que el mismo código funcione en desarrollo y producción:

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)');
}

Desarrollo con certificados autofirmados

Para desarrollo local, genera un certificado autofirmado (no para producción):

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'),
};

Tu navegador mostrará una advertencia (los certificados autofirmados no son de confianza); haz clic para continuar durante el desarrollo.

Recarga de certificados en caliente

Para recargar certificados sin reiniciar (útil para la renovación de Let’s Encrypt):

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);

Esto vuelve a leer los archivos en cada nueva conexión. Para mejor rendimiento, añade un observador de archivos que recargue solo cuando los archivos cambien.

Preguntas frecuentes

¿Debo manejar SSL en Node.js o usar un proxy inverso?

Para producción: usa un proxy inverso. Para desarrollo, proyectos pequeños o servicios internos: Node.js con HTTPS directo está bien. El patrón de proxy inverso es estándar porque separa responsabilidades y maneja TLS más eficientemente.

¿Puedo usar certificados de GetHTTPS con Node.js?

Sí. GetHTTPS produce archivos PEM estándar (privkey.pem, fullchain.pem) que Node.js lee directamente con fs.readFileSync().

¿Cómo manejo la renovación de certificados en Node.js?

Si usas un proxy inverso, simplemente reemplaza los archivos y recarga el proxy; Node.js no necesita reiniciarse. Si manejas TLS directamente, usa el enfoque de SNICallback mostrado arriba o reinicia el proceso de Node.js después de reemplazar los archivos del certificado.

¿Qué hay de SSL en localhost para desarrollo?

Usa un certificado autofirmado (mostrado arriba) o mkcert para un certificado de desarrollo localmente confiable. No uses Let’s Encrypt para localhost: no puede validar un dominio que no es accesible públicamente.

¿Puedo usar certificados de GetHTTPS con Deno o Bun?

Sí. Tanto Deno como Bun soportan TLS con archivos PEM:

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"); },
});

Los mismos archivos PEM de GetHTTPS funcionan en todos los runtimes de JavaScript.

Artículos relacionados

Primeros pasos 2026-05-08
Cómo obtener un certificado SSL gratuito (guía paso a paso)
Obtén un certificado SSL gratuito de Let's Encrypt en 5 minutos — sin software que instalar, sin cuenta que crear. Guía completa con 4 métodos, ambos tipos de desafío, instalación en 6 plataformas y solución de problemas.
Despliegue 2026-05-08
Certificados SSL con Docker y proxies inversos
Configura HTTPS para contenedores Docker usando proxy inverso Nginx, Traefik con Let's Encrypt automático, o montaje manual de certificados.
SSL y certificados 2026-05-07
Formatos de certificados SSL: PEM, PFX, DER explicados
Comprende los formatos de certificado PEM, PFX/PKCS#12 y DER. Aprende qué formato necesita tu servidor y cómo convertir entre ellos con OpenSSL.
Obtén un certificado SSL gratuito en tu navegador
Sin instalación, sin cuenta. Tu clave privada nunca sale de tu dispositivo.
Obtener certificado