Node.js で HTTPS サーバーを立てる (Express 対応)

Node.js の http サーバーに自己署名サーバー証明書と秘密鍵を適用し https 対応する方法を一次情報を明記しながら紹介します。また Express を使用する際の対応も併せて紹介します。

やりたいこと

開発用にさくっと Node.js で HTTPS サーバーを用意したかったのでやってみました。

環境

証明書作成環境

OS
Ubuntu 22.04.4 LTS
OpenSSL
OpenSSL 3.0.2 15 Mar 2022 (Library: OpenSSL 3.0.2 15 Mar 2022)

Windows で OpenSSL 無しで PEM 形式の証明書と秘密鍵を作成する方法をこちらの記事 PowerShell 版Node.js 版 で紹介していますので、必要であればご確認ください。

Node.js 稼働環境

OS
Microsoft Windows 10 22H2
nodejs
v20.10.0
express
4.19.2

Node.js で HTTPS サーバを起動する方法

準備

とりあえず自己署名証明書(所謂オレオレ証明書)を用意します。ここでは openssl を使用します。繰り返しになりますが Windows で OpenSSL 無しで PEM 形式の証明書と秘密鍵を作成する方法をこちらの記事 PowerShell 版Node.js 版 で紹介していますので、必要に応じて参考にしてください。

  1. 秘密鍵を作成します。

    openssl genpkey -algorithm rsa -out priv.key -pkeyopt rsa_keygen_bits:2048   
  2. 署名リクエストを作成します。

    openssl req -new -key priv.key -sha256 -subj "/C=JP/ST=Tokyo/CN=xxx.xxx.xxx.xxx" -out server.csr
  3. サブジェクト代替名 (Subject Alternative Name) を追加するためのファイルを作成します。

    echo "subjectAltName=IP:xxx.xxx.xxx" > san.txt
  4. 証明書を作成します。

    openssl x509 -in server.csr -out server.crt -req -signkey priv.key -days 3650 -sha256 -extfile san.txt

基本ここまでで PEM ベースの秘密鍵と証明書が揃っているので後続作業に進めますが、PEM ベースではなく PKCS #12 の PFX ファイルベースでの HTTPS 化の方法も紹介しますので、PFX ファイルも作成しておきます。

openssl pkcs12 -export -inkey priv.key -in server.crt -out server.pfx

上記コマンド実行時にパスワード設定が求められます。ここで設定したパスワードは後ほど使用しますので、忘れないようにしてください。

Enter Export Password:
Verifying - Enter Export Password:

PEM ベースの秘密鍵と証明書を使用して HTTPS サーバーを起動する

PEM ベースでも PFX ベースでも Node.js のビルトインモジュール httpscreateServer() メソッドを使用します。

https.createServer([options][, requestListener])

  • options <Object> Accepts options from tls.createServer(), tls.createSecureContext() and http.createServer().
  • requestListener <Function> A listener to be added to the 'request' event.
  • Returns: <https.Server> - HTTPS | Node.js v22.1.0 Documentation

まず PEM ベースの秘密鍵と証明書を使用した HTTPS サーバーの起動方法です。

上記 createServer() の引数 optiontls.createServer(), tls.createSecureContext(), http.createServer() の引数 option の和集合とありますが、そのうちの tls.createSecureContext() の引数 option を確認します。

すると certkey というプロパティが見つかると思います。

cert | <string[]> | | <Buffer[]> Cert chains in PEM format.
TLS (SSL) | Node.js v22.1.0 Documentation

key | <string[]> | | <Buffer[]> | <Object[]> Private keys in PEM format. PEM allows the option of private keys being encrypted. Encrypted keys will be decrypted with options.passphrase.
TLS (SSL) | Node.js v22.1.0 Documentation

cert プロパティーに PEM 形式の証明書、key プロパティーに PEM 形式の秘密鍵を設定して、createServer() メソッドの option 引数に渡すことで HTTP サーバーを起動できます。

import { readFileSync } from 'node:fs';
import { createServer } from 'node:https';

const options = {
  key: readFileSync('priv.key'),
  cert: readFileSync('server.crt'),
};

createServer(options, (req, res) => {
  res.writeHead(200);
  res.end('hello world\n');
}).listen(8000); 

なお、秘密鍵にパスワードを設定している場合は、passphrase プロパティーで指定します。

passphrase Shared passphrase used for a single private key and/or a PFX.
TLS (SSL) | Node.js v22.1.0 Documentation

PFX ファイルを使用して HTTPS サーバーを起動する

次に PFX ベースで秘密鍵とサーバー証明書を指定する方法です。こちらも https.createServer()option 引数の pfx プロパティーで指定します。また PFX はパスワードによる暗号化が必須なので、複合するためのパスワードを passphrase プロパティーに指定します。

pfx | <string[]> | | <Buffer[]> | <Object[]> PFX or PKCS12 encoded private key and certificate chain. pfx is an alternative to providing key and cert individually. PFX is usually encrypted, if it is, passphrase will be used to decrypt it.
TLS (SSL) | Node.js v22.1.0 Documentation

import { readFileSync } from 'node:fs';
import { createServer } from 'node:https';

const options = {
  pfx: readFileSync('./server.pfx'),
  passphrase: 'password',
};

createServer(options, (req, res) => {
  res.writeHead(200);
  res.end('hello world\n');
}).listen(8000); 

(おまけ) Express の HTTPS 化

Express はバージョン 4 から Node.js ビルトインの http モジュールの使用が必須ではなくなりました。ただしいくつかのユースケースにおいては引き続き Node.js ビルトインのモジュールが必要となります。HTTPS 化もそのうちの一つです。

http モジュールは、このモジュールを直接処理する必要がある場合を除き、不要になりました (socket.io/SPDY/HTTPS)。アプリケーションは、app.listen() 関数を使用して開始できます。
Express 4 への移行

したがってここまで紹介した Node.js ビルトインの https モジュールを使用する方法がそのまま使えます。https.createServer() メソッドの requestListener 引数に渡すハンドラーが Express になるだけです。

import { readFileSync } from 'node:fs';
import { createServer } from 'node:https';
import express from 'express';

const app = express();

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

const options = {
  key: readFileSync('priv.key'),
  cert: readFileSync('server.crt'),
};

createServer(options, app).listen(8000); 

あとがき

Node.js はビルトインモジュールだけで簡単に HTTPS サーバーを立てることができるので開発中やテストにさくっと HTTPS サーバーが欲しいというときに非常に便利だなと思いました。