目次
「ローカルでは動くのに、本番に出したらログインのCookieが保存されない」「Service Workerがlocalhost以外で登録できない」「OAuthのリダイレクト先にhttpが指定できず、開発中に認証を試せない」。本番はHTTPS、開発はHTTPという二重構成が原因で、本番デプロイ後に初めて気づくバグは少なくありません。
この記事では、ローカル開発環境をHTTPS前提にすべき理由と、mkcertでローカルCAを作って信頼させる仕組み、hostsファイルで独自ドメインを割り当てる方法、Vite・Next.js・Docker内での証明書の指定方法、そして自己署名証明書の警告を「無視する」癖がなぜ危険かを解説します。HTTPSと証明書の仕組み自体はSSL/TLS証明書の仕組み入門で扱っているため、ここでは開発環境に特化します。
なぜローカル開発もHTTPSにすべきか
ブラウザの挙動がhttpとhttpsで変わる機能
モダンブラウザは「安全なコンテキスト(Secure Context)」でしか動かないAPIを増やし続けています。localhostは例外的に安全なコンテキスト扱いですが、独自ドメインやLAN内のIPでアクセスした瞬間に制限がかかります。
- Secure属性付きCookie:HTTPS以外では保存されない。SameSite=Noneを使う場合はSecureが必須
- Service Worker・Push通知・PWA:HTTPS(またはlocalhost)でのみ登録可能
- getUserMedia(カメラ・マイク)・Geolocation・Clipboard API:安全なコンテキスト限定
- OAuth/OIDCのリダイレクトURI:多くのIdPがhttpのリダイレクト先をlocalhost以外で拒否する
- HSTS:本番ドメインのサブドメインでincludeSubDomainsを有効にしていると、dev.example.comへのhttp接続をブラウザが拒否する
「localhostなら動く」で済ませていると、本番と同じドメイン構成(サブドメイン間のCookie共有など)を開発中に再現できず、環境差分による不具合が本番で初めて露見します。
自己署名証明書の警告を「無視する」癖の危険
opensslで作った自己署名証明書を使い、ブラウザの警告画面で「詳細設定→アクセスする」を毎回クリックする運用は、二重の意味で危険です。ひとつは、開発者自身が警告を無視する行動を習慣化してしまい、本物の中間者攻撃やフィッシングの警告も同じ手つきでスキップしてしまうこと。もうひとつは、警告を通過したページではHSTSやService Workerなど一部機能がそもそも動かないため、結局本番と同じ挙動を再現できていないことです。証明書は「警告を出さずに信頼される」状態で使わなければ、HTTPS化した意味が半減します。
mkcertでローカルCAを作り、信頼された証明書を発行する
mkcertの仕組み
mkcertは、開発マシン専用のプライベートCA(認証局)を作成し、そのルート証明書をOSとブラウザの信頼ストアに登録したうえで、任意のホスト名向けの証明書を発行するツールです。「自分のマシンが信頼するCAが署名した証明書」なので、ブラウザは警告を出しません。CAの秘密鍵は自分のマシンにしかないため、他人がこのCAで偽の証明書を作ることはできません。
導入手順
- mkcertをインストールします(macOSはHomebrew、Linuxは各ディストリのパッケージまたはバイナリ)。
- mkcert -install でローカルCAを作成し、システムとFirefoxの信頼ストアに登録します。
- 使いたいホスト名を列挙して証明書と秘密鍵を発行します。
# macOS
brew install mkcert nss # nssはFirefox用
mkcert -install
# ローカルCAの場所を確認(rootCA.pem / rootCA-key.pem)
mkcert -CAROOT
# 複数のホスト名・ワイルドカード・IPをまとめて1枚に
mkcert dev.example.test "*.dev.example.test" localhost 127.0.0.1 ::1
# → dev.example.test+4.pem と dev.example.test+4-key.pem が生成される
rootCA-key.pemは絶対にGitにコミットせず、他人に渡さないでください。この鍵を持つ人はあなたのブラウザが信頼する任意のドメインの証明書を作れます。ワイルドカード証明書を発行しておくと、サブドメインを増やすたびに再発行する手間が省けます。
hostsファイルで独自ドメインをローカルに割り当てる
.testドメインを使う理由
本番と同じ「app.example.com と api.example.com でCookieを共有する」構成を再現するには、ローカルでもサブドメインが必要です。/etc/hosts(Windowsは C:\Windows\System32\drivers\etc\hosts)に名前と127.0.0.1の対応を書けば、DNSを引かずにその名前が自分を指すようになります。
# /etc/hosts に追記(sudoが必要)
127.0.0.1 dev.example.test api.dev.example.test admin.dev.example.test
::1 dev.example.test api.dev.example.test admin.dev.example.test
トップレベルドメインは.testか.localhostを使ってください。.devは実在するTLDでHSTSプリロードに登録されているため、ブラウザがhttpsを強制し、証明書がないと開けなくなります。.localもmDNS(Bonjour)と衝突して名前解決が不安定になることがあります。hostsはワイルドカードを書けないので、サブドメインを増やしたら1行ずつ追加が必要です。名前解決の仕組みそのものはDNSの仕組みで解説しています。
開発サーバーとDockerで証明書を使う
ViteとNext.jsでの指定
// vite.config.ts
import fs from 'node:fs';
import { defineConfig } from 'vite';
export default defineConfig({
server: {
host: 'dev.example.test',
port: 5173,
https: {
key: fs.readFileSync('./certs/dev.example.test+4-key.pem'),
cert: fs.readFileSync('./certs/dev.example.test+4.pem'),
},
},
});
# Next.js(v13.5以降)は開発サーバーが自前でmkcertを呼び出せる
next dev --experimental-https
# 既存の証明書を指定する場合
next dev --experimental-https \
--experimental-https-key ./certs/dev.example.test+4-key.pem \
--experimental-https-cert ./certs/dev.example.test+4.pem
フレームワークごとに設定するより、手元でもNginxやCaddyをTLS終端として置き、後ろの開発サーバーはhttpのままにする構成の方が本番に近く、複数サービスをまとめて扱えます。リバースプロキシとしてのNginx・Caddyを使えば、Caddyに至っては内部CAを自動生成して証明書も自前で管理してくれます。
Dockerコンテナ内での扱い
コンテナ内で動くサービスにmkcertの証明書を使う場合は、証明書ファイルをボリュームでマウントします。注意点は、コンテナ内のcurlやNode.jsがホストのローカルCAを信頼していないことです。フロントのコンテナからhttps://api.dev.example.testを呼ぶと証明書検証で失敗します。rootCA.pem(秘密鍵ではなく公開側)をコンテナに渡し、環境変数で信頼させます。
# docker-compose.yml(抜粋)
services:
web:
build: .
volumes:
- ./certs:/certs:ro
- ${HOME}/Library/Application Support/mkcert/rootCA.pem:/usr/local/share/ca-certificates/mkcert.crt:ro
environment:
NODE_EXTRA_CA_CERTS: /usr/local/share/ca-certificates/mkcert.crt
SSL_CERT_FILE: /usr/local/share/ca-certificates/mkcert.crt # Python/curl向け
extra_hosts:
- "api.dev.example.test:host-gateway"
コンテナ内からdev.example.testという名前でホスト側のNginxに届くようにするには、コンテナのhosts解決もextra_hostsで補う必要があります。コンテナのネットワークで「つながらない」場合の切り分けはlocalhost・0.0.0.0・127.0.0.1の違いを参照してください。
トラブル事例:Firefoxだけ警告が消えない
症状:mkcert -installを実行し、ChromeとSafariでは鍵マークが出るのに、Firefoxだけ「この接続は安全ではありません」の警告が出続ける。
原因:FirefoxはOSの証明書ストアではなく独自のNSSデータベースを使う。macOSでnssパッケージ(certutilコマンド)が入っていなかったため、mkcert -installがFirefoxへの登録をスキップしていた。Linuxでも同様にlibnss3-toolsが必要。
対処:brew install nss(Debian系は apt install libnss3-tools)の後に再度 mkcert -install を実行。Firefoxを再起動して解消した。同様に、Java製ツールやPythonのrequestsは独自の信頼ストア(cacerts/certifi)を使うため、それぞれの方法でrootCA.pemを追加する必要があります。
まとめ
ローカル開発をHTTPS化する目的は「本番と同じブラウザの挙動で開発する」ことです。Secure Cookie・Service Worker・OAuthリダイレクトなど、httpでは再現できない機能は多く、警告を無視して通す運用ではその目的を果たせません。mkcertでローカルCAを作り、hostsで.testドメインを割り当て、開発サーバーかリバースプロキシで証明書を指定し、コンテナにはrootCA.pemを信頼させる。この4ステップを一度整えれば、チーム全員が同じ手順で本番相当の環境を再現できます。開発環境の標準化や本番と揃えた構成づくりでお困りの場合は、Harmonic Societyのシステム開発・インフラ支援にご相談ください。
Harmonic Society
この記事の内容、自社の業務でも活かせそうですか?
ローカルLLM・AI・クラウドなどの技術導入を、要件整理からPoC・社内展開まで代表エンジニアが伴走します。オンライン対応・全国OK。まずは30分の無料相談から。売り込みはしません。
関連記事
Related / 9 articles
Notes & Insights
- プログラミング
DDoS攻撃の仕組みと対策入門|レイヤー別の防御とCDN・クラウドの活用
DDoS攻撃をボリューム型・プロトコル型・アプリ層に分けて仕組みを解説し、自前サーバーで防げない理由、CloudflareやAWS Shieldの標準防御、オリジンIPの隠し方、レートリミットとBot対策、攻撃を受けたときの初動、費用が跳ね上がるDenial of Walletへの備えまでわかります。
- プログラミング
WAFとは?仕組み・導入パターン・誤検知対策|Webアプリを攻撃から守る実践ガイド
WAFがファイアウォールやIDSと何が違うのか、シグネチャとマネージドルールの仕組み、Cloudflare WAF・AWS WAF・ModSecurityの比較、フォーム送信がブロックされる誤検知の調査と例外設定、ログ監視、WAFが代替できないことまで実践的に解説します。
- プログラミング
セキュリティヘッダー入門|CSP・HSTS・X-Frame-Optionsの設定と効果を実践解説
CSP・HSTS・X-Frame-Options・X-Content-Type-Optionsなど主要セキュリティヘッダーが防ぐ攻撃と、CSPのReport-Onlyからの段階導入、nonce/hash、HSTS preloadの不可逆リスク、Nginx・Next.js・Astroでの設定例、確認方法を解説します。
- プログラミング
クラウドの通信費(Egress)入門|データ転送量課金の仕組みと転送コストを抑える設計
クラウドの「受信無料・送信有料」の原則、AZ間・リージョン間・インターネット向けの単価差、NATゲートウェイ処理料の罠、CDNで転送量を減らす方法、バックアップやログ転送の見落とし、請求書で転送料を特定する手順を解説。想定外の請求を防げます。
- プログラミング
秘密情報をGitに入れない仕組み|.gitignore・git-secrets・履歴から漏れた鍵の削除
APIキーや.envをGitにコミットしてしまう典型経路と、.gitignore・.env.exampleの運用、pre-commitでのgitleaks検知、GitHub secret scanningの活用、漏れた鍵の無効化と履歴書き換え(git filter-repo)の手順を解説。仕組みで再発を防げます。
- プログラミング
開発・ステージング・本番環境の分離設計|環境差分をなくす構成とアクセス制御
開発・ステージング・本番それぞれの目的と、構成をコードで揃える方法、環境別の設定注入、本番データを使わないテストデータ戦略、ステージングの保護(Basic認証・IP制限・noindex)、コストを抑える運用までを解説。環境差分による本番障害を防げます。
- プログラミング
ngrok・Cloudflare Tunnelでローカルを公開|Webhook開発とデモ環境の作り方
NAT内のローカル環境にStripeやLINEのWebhookを届けるトンネリングの仕組みを解説。ngrok・Cloudflare Tunnel・localtunnelの比較、固定ドメインと認証、リクエスト検査、公開時のセキュリティ、自宅サーバー公開への応用までわかります。
- プログラミング
localhost・0.0.0.0・127.0.0.1の違い|ポートとUnixソケットを理解して「つながらない」を解決
localhost・127.0.0.1・0.0.0.0の意味の違い、Dockerで外から接続できない原因、host.docker.internal、ポート競合の調べ方、Unixソケットの利点と権限、1024未満ポートの制約を解説。「つながらない」を仕組みから解決できます。
- プログラミング
GitOps入門|ArgoCD・FluxでGitをインフラの正とするデプロイ運用
GitOpsのPush型とPull型の違い、差分検知と自動同期の仕組み、リポジトリ構成、ArgoCDの最小導入手順までを解説。git revertで戻せるデプロイ運用が理解でき、Kubernetes以外への応用の考え方もわかります。
Harmonic Society
「読んで終わり」にせず、自社の業務で試してみませんか?
AI・ローカルLLM・クラウドの導入を、要件整理からPoC・社内展開まで代表エンジニアが伴走します。オンライン対応・全国OK・売り込みなし。
無料・30分・オンラインOK|1営業日以内に返信します