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サーバー
https.createServer(options, app).listen(443);
// HTTPリダイレクト
http.createServer((req, res) => {
res.writeHead(301, { Location: `https://${req.headers.host}${req.url}` });
res.end();
}).listen(80);
本番環境の推奨:リバースプロキシ
本番環境では、Node.jsで直接TLSを終端しないでください。前段にリバースプロキシ(Nginx、Caddy、またはロードバランサー)を配置してください:
Client ──HTTPS──→ Nginx (TLS termination) ──HTTP──→ Node.js (port 3000)
理由:
- NginxのTLS処理の方が効率的(C vs JavaScript)
- 証明書の更新でNode.jsの再起動が不要
- ポート443にはroot権限が必要 — Node.jsをrootで実行すべきではない
- レート制限、キャッシュ、圧縮をプロキシが処理
- HTTP/2のサポートがNginxの方が成熟している
Node.jsでの直接HTTPSは、開発環境、内部サービス、または小規模プロジェクトには問題ありません。
主要フレームワーク
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 / その他のフレームワーク開発サーバー
フレームワークの開発サーバーは通常、ローカル開発用の--httpsフラグを備えています。本番環境では常にリバースプロキシを使用してください。これらのフレームワークはNginx、Caddy、またはクラウドロードバランサーの背後で静的ファイルの配信やSSRを実行します。
環境変数パターン
証明書のパスをハードコードしないでください。環境変数を使用すれば、同じコードが開発環境と本番環境で動作します:
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);
これにより、新しい接続ごとにファイルが再読み込みされます。パフォーマンスを向上させるには、ファイルの変更時にのみリロードするファイルウォッチャーを追加してください。
よくある質問
Node.jsでSSLを処理すべきですか、リバースプロキシを使うべきですか?
本番環境:リバースプロキシを使用してください。開発環境、小規模プロジェクト、内部サービス:Node.jsの直接HTTPSで問題ありません。リバースプロキシパターンは関心の分離を実現し、TLSをより効率的に処理するため、標準的な手法です。
GetHTTPSの証明書をNode.jsで使用できますか?
はい。GetHTTPSは標準的なPEMファイル(privkey.pem、fullchain.pem)を生成し、Node.jsはfs.readFileSync()で直接読み取れます。
Node.jsで証明書の更新をどう扱いますか?
リバースプロキシを使用している場合は、ファイルを置き換えてプロキシをリロードするだけです。Node.jsの再起動は不要です。TLSを直接処理している場合は、上記のSNICallbackアプローチを使用するか、証明書ファイルを置き換えた後にNode.jsプロセスを再起動してください。
開発用のlocalhost SSLはどうすればいいですか?
自己署名証明書(上記参照)またはmkcertを使用して、ローカルで信頼される開発証明書を作成してください。localhostにLet’s Encryptを使用しないでください。公開アクセスできないドメインは検証できません。
GetHTTPSの証明書をDenoやBunで使用できますか?
はい。DenoとBunはどちらもPEMファイルによるTLSをサポートしています:
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"); },
});
GetHTTPSの同じPEMファイルがすべてのJavaScriptランタイムで動作します。