Node.js имеет встроенную поддержку HTTPS через модуль https. Вы загружаете файлы сертификатов и создаёте HTTPS-сервер. Это руководство охватывает базовый Node.js, Express и рекомендуемую конфигурацию для продакшена.
Базовый HTTPS-сервер
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 с 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');
});
Перенаправление HTTP на HTTPS
Запустите одновременно HTTP- и HTTPS-серверы:
const http = require('http');
const https = require('https');
const fs = require('fs');
const express = require('express');
const app = express();
// ... ваши маршруты
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);
Рекомендация для продакшена: обратный прокси
Для продакшена не завершайте TLS непосредственно в Node.js. Используйте обратный прокси (Nginx, Caddy или балансировщик нагрузки) перед приложением:
Client ──HTTPS──→ Nginx (TLS termination) ──HTTP──→ Node.js (port 3000)
Причины:
- Nginx обрабатывает TLS эффективнее (C против JavaScript)
- Обновление сертификатов не требует перезапуска Node.js
- Порт 443 требует прав root — Node.js не должен работать от root
- Ограничение скорости, кеширование, сжатие обрабатываются прокси
- HTTP/2 поддержка более зрелая в Nginx
Прямой HTTPS в Node.js подходит для разработки, внутренних сервисов или небольших проектов.
Популярные фреймворки
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 / другие фреймворки (dev-серверы)
Dev-серверы фреймворков обычно имеют флаг --https для локальной разработки. Для продакшена всегда используйте обратный прокси — эти фреймворки раздают статические файлы или выполняют SSR за Nginx, Caddy или облачным балансировщиком нагрузки.
Паттерн с переменными окружения
Не хардкодьте пути к сертификатам. Используйте переменные окружения, чтобы один и тот же код работал в среде разработки и продакшена:
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)');
}
Разработка с самоподписанными сертификатами
Для локальной разработки сгенерируйте самоподписанный сертификат (не для продакшена):
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'),
};
Браузер покажет предупреждение (самоподписанные сертификаты не являются доверенными) — пропустите его для целей разработки.
Горячая перезагрузка сертификатов
Для перезагрузки сертификатов без перезапуска (полезно при обновлении 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);
Это перечитывает файлы при каждом новом соединении. Для лучшей производительности добавьте отслеживание файлов, чтобы перезагружать их только при изменении.
Часто задаваемые вопросы
Обрабатывать SSL в Node.js или использовать обратный прокси?
Для продакшена: используйте обратный прокси. Для разработки, небольших проектов или внутренних сервисов: прямой HTTPS в Node.js вполне подходит. Паттерн с обратным прокси является стандартным, поскольку разделяет ответственности и обрабатывает TLS более эффективно.
Можно ли использовать сертификаты GetHTTPS с Node.js?
Да. GetHTTPS создаёт стандартные PEM-файлы (privkey.pem, fullchain.pem), которые Node.js читает напрямую через fs.readFileSync().
Как обрабатывать обновление сертификатов в Node.js?
Если используется обратный прокси, просто замените файлы и перезагрузите прокси — Node.js не нужно перезапускать. Если TLS обрабатывается напрямую, используйте подход с SNICallback, описанный выше, или перезапустите процесс Node.js после замены файлов сертификатов.
А как насчёт SSL для localhost при разработке?
Используйте самоподписанный сертификат (показан выше) или mkcert для получения локально доверенного сертификата разработки. Не используйте Let’s Encrypt для localhost — он не может подтвердить домен, который недоступен публично.
Можно ли использовать сертификаты GetHTTPS с Deno или Bun?
Да. И Deno, и Bun поддерживают TLS с 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"); },
});
Одни и те же PEM-файлы от GetHTTPS работают во всех средах выполнения JavaScript.