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();
// ... 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);
生產環境建議:反向代理
在生產環境中,不要直接在 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。
可以在 Node.js 中使用 GetHTTPS 證書嗎?
可以。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——它無法驗證非公開可訪問的域名。
可以在 Deno 或 Bun 中使用 GetHTTPS 證書嗎?
可以。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 執行時。