最新のWeb API(Service Worker、Geolocation、Clipboard、Camera)はHTTPSを必要とします。開発中でも同様です。このガイドでは、ブラウザ警告なしでlocalhostに信頼されたHTTPSを設定する方法を解説します。
Let’s EncryptをlocalhostでNは使えない理由
Let’s Encryptは、サーバー上のファイルまたはDNSレコードを確認してドメインの所有権を検証します。localhostは実際のドメインではなく、あなたのマシン上でのみ127.0.0.1に解決されます。localhostを「所有」していることを誰も検証できないため、公的な認証局はlocalhostの証明書を発行しません。
localhostでHTTPSを使用する選択肢:
- mkcert — ローカルで信頼される証明書を作成(推奨)
- 自己署名証明書 — 動作するがブラウザ警告が表示される
- トンネリングサービス — ngrok、Cloudflare Tunnel(実際のドメイン、実際の証明書)
オプション1: mkcert(推奨)
mkcertは、マシン上にローカル認証局を作成し、システムのトラストストアに追加し、そのCAが署名した証明書を発行します。ブラウザはこれらの証明書を警告なしで信頼します。
mkcertのインストール
# macOS
brew install mkcert
# Linux (Firefoxにはlibnss3-toolsが必要)
sudo apt install libnss3-tools
brew install mkcert # またはGitHub releasesからダウンロード
# Windows
choco install mkcert
# または: scoop install mkcert
ローカルCAのセットアップ
mkcert -install
# Output: Created a new local CA
# The local CA is now installed in the system trust store
これにより、ルート証明書がシステムとブラウザのトラストストアに追加されます。一度だけ実行すれば、以降のすべての証明書に適用されます。
証明書の生成
# localhostの場合
mkcert localhost 127.0.0.1 ::1
# カスタムローカルドメインの場合
mkcert myapp.test "*.myapp.test" localhost 127.0.0.1
# Output:
# Created a new certificate valid for the following names:
# - "localhost"
# - "127.0.0.1"
# - "::1"
# The certificate is at "./localhost+2.pem" and the key at "./localhost+2-key.pem"
開発サーバーで使用する
Node.js/Express:
const https = require('https');
const fs = require('fs');
const app = require('./app');
https.createServer({
key: fs.readFileSync('./localhost+2-key.pem'),
cert: fs.readFileSync('./localhost+2.pem'),
}, app).listen(3000);
Nginx(ローカル):
server {
listen 443 ssl;
server_name localhost;
ssl_certificate /path/to/localhost+2.pem;
ssl_certificate_key /path/to/localhost+2-key.pem;
# ...
}
Vite / webpack開発サーバー:
// vite.config.js
import fs from 'fs';
export default {
server: {
https: {
key: fs.readFileSync('./localhost+2-key.pem'),
cert: fs.readFileSync('./localhost+2.pem'),
},
},
};
セキュリティ上の注意
mkcertのルートCA鍵を共有しないでください(mkcert -CAROOTにあるrootCA-key.pem)。これを持つ人は誰でも、あなたのマシン上で任意のドメインの信頼された証明書を作成できます。実質的にローカルでの中間者攻撃が可能になります。
mkcertは開発専用です。本番環境ではLet’s Encryptを使用してください。
オプション2: 自己署名証明書
mkcertをインストールできない場合、OpenSSLで自己署名証明書を生成します:
openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:P-256 \
-keyout localhost-key.pem -out localhost-cert.pem \
-days 365 -nodes -subj "/CN=localhost" \
-addext "subjectAltName=DNS:localhost,IP:127.0.0.1"
欠点: 自己署名証明書はどの認証局にも信頼されていないため、ブラウザがセキュリティ警告を表示します。毎回警告をクリックして進む必要があります(または例外を追加)。
オプション3: トンネリング(実際のドメイン + 実際の証明書)
トンネリングサービスを使用して、ローカルサーバーを実際のドメインと証明書で公開します:
# ngrok
ngrok http 3000
# 取得: https://abc123.ngrok.io
# Cloudflare Tunnel
cloudflared tunnel --url http://localhost:3000
# 取得: https://random-name.trycloudflare.com
適している場合: Webhookのテスト、チームメイトとの共有、HTTPSを必要とするOAuthコールバックのテスト。
mkcert vs 自己署名 vs トンネリング
| mkcert | 自己署名 | トンネリング | |
|---|---|---|---|
| ブラウザ警告 | なし | あり(毎セッション) | なし |
| セットアップ工数 | 低(1回のインストール) | 低(1コマンド) | 低 |
| オフライン動作 | ✅ | ✅ | ❌ |
| パフォーマンス | ローカル速度 | ローカル速度 | ネットワーク遅延 |
| カスタムドメイン | ✅(*.testなど) | ✅ | プロバイダー割当 |
| 最適な用途 | 日常の開発 | 簡易的な一回限り | Webhook、共有 |
よくある質問
mkcert証明書をチーム全体で共用できますか?
ルートCA鍵を共有しないでください。各開発者が自分のマシンでmkcert -installを実行し、独自のローカルCAを作成してください。生成された証明書(.pemファイル)は共有できますが、信頼するためには各自がCAをインストールする必要があります。
mkcertはDockerで動作しますか?
はい。証明書ファイルをコンテナにマウントし、サーバー設定で参照してください。コンテナ自体にmkcertをインストールする必要はありません。.pemファイルだけで十分です。ブラウザが動作するホストマシンにmkcertのCAがインストールされている必要があります。
どのローカルドメインを使うべきですか?
.test(例:myapp.test)を使用してください。IETFがテスト用に予約しており、実際のドメインと競合することはありません。.dev(Googleが所有)や.local(mDNSで使用)は避けてください。/etc/hostsにエントリを追加して、.testドメインが127.0.0.1を指すようにしてください。
localhost HTTPSから本番HTTPSに切り替えるにはどうすればいいですか?
これらは完全に別のものです。localhostの証明書(mkcert/自己署名)は開発マシンに残ります。本番環境ではLet’s Encrypt(GetHTTPS経由)で実際の証明書を取得してください。コードで証明書のパスを環境変数から読み取るようにすれば、設定を変更するだけで切り替えられます。