mkcert: локальные HTTPS-сертификаты для разработки

10 минут чтения
Средний рейтинг статьи — 4.8

mkcert — утилита для выпуска локальных HTTPS-сертификатов, которым доверяет ваша рабочая машина. Она решает частую проблему разработки: приложение должно работать как в продакшене, по https://, но браузер ругается на самоподписанный сертификат, cookies ведут себя иначе, Service Worker не запускается, а OAuth callback ломается на редиректе.

В отличие от ручной генерации через openssl, mkcert избавляет от возни с локальным CA, SAN-полями и хранилищами доверия. Утилита создаёт локальный корневой центр сертификации, добавляет его в системное хранилище и выпускает сертификаты для localhost, 127.0.0.1, *.test и внутренних dev-доменов.

Для разработчиков и DevOps это способ приблизить dev-среду к боевой: проверить TLS, secure cookies, CORS, WebSockets, HTTP/2, reverse proxy и взаимодействие сервисов до деплоя. Дальше — как устроен mkcert, как его поставить, как использовать с Node.js, Nginx и Docker и какие у него ограничения.

Зачем нужен mkcert в локальной разработке

Многие веб-фичи завязаны на защищённый контекст. Если проект запускается только по http://localhost:3000, часть поведения отличается от продакшена.

1) Secure cookies — cookies с флагом Secure браузер отправляет только по HTTPS. Это критично для сессий, JWT refresh token, CSRF-токенов и OAuth-флоу. На HTTP вы либо отключаете защиту в dev-режиме, либо тестируете не тот сценарий, что будет в продакшене.

2) Service Worker и PWA — Service Worker требует HTTPS, исключение сделано только для localhost. Но как только вы используете локальный домен вроде app.test или client.local, понадобится валидный сертификат.

3) OAuth и внешние интеграции — многие провайдеры требуют https:// в redirect URI. Даже если localhost разрешён, команда часто тестирует домены вида auth.myapp.test, api.myapp.test, admin.myapp.test.

4) WebSockets и mixed content — если фронтенд открыт по HTTPS, подключение к ws:// может быть заблокировано. Нужно тестировать wss://, а значит — TLS на локальном сервере или reverse proxy.

5) Reverse proxy и микросервисы — в локальном окружении часто поднимают несколько сервисов: фронтенд, API, auth, gateway, mock-сервер, очередь, база. mkcert позволяет собрать рабочий HTTPS-контур для такого dev-кластера без публичного домена и без Let’s Encrypt.

Если нужно освежить базу по TLS, сертификатам и цепочкам доверия, сначала почитайте, как работает HTTPS. mkcert не заменяет эти знания, но заметно упрощает рутину.

Как mkcert работает под капотом

mkcert создаёт локальный корневой сертификат (CA) с приватным ключом и устанавливает его в хранилища доверия операционной системы и, где возможно, браузеров. После этого любые сертификаты, выпущенные этим CA, считаются доверенными на вашей машине.

Схема простая:

1) Локальный CA — команда mkcert -install создаёт root CA. Это не публичный центр сертификации, а издатель, которому доверяет только ваша машина.

2) Установка доверия — mkcert добавляет корневой сертификат в системный trust store. На macOS это Keychain, на Windows — системное хранилище сертификатов, на Linux — NSS/system trust store в зависимости от окружения.

3) Выпуск leaf-сертификатов — команда mkcert localhost app.test "*.app.test" создаёт сертификат и ключ для указанных имён. В сертификат попадают SAN-записи: DNS-имена и IP-адреса.

4) Использование в сервере — вы передаёте .pem и -key.pem в Node.js, Nginx, Caddy, Traefik, Vite, webpack dev server или другой сервер с поддержкой HTTPS.

Принципиальное отличие от обычного самоподписанного сертификата: браузер доверяет не самому leaf-сертификату, а вашему локальному CA. Поэтому не нужно нажимать «продолжить небезопасно», импортировать сертификат вручную или отключать проверки.

Ручной вариант через OpenSSL тоже рабочий, особенно если нужно разобраться в деталях. У Statuser есть отдельная инструкция по созданию самоподписанного SSL-сертификата с помощью OpenSSL. Но для повседневной разработки mkcert обычно быстрее и безопаснее: меньше шансов забыть subjectAltName, выпустить сертификат не на тот домен или положить приватный ключ в репозиторий.

Установка mkcert на macOS, Linux и Windows

Перед началом проверьте, что у вас есть права администратора: установка локального CA требует изменения системного хранилища доверия.

Для macOS самый простой путь — Homebrew:

brew install mkcert
brew install nss
mkcert -install

Пакет nss нужен, чтобы mkcert мог добавить CA в хранилище Firefox. Chrome и Safari обычно используют системное хранилище macOS.

Для Windows можно использовать winget, Chocolatey или Scoop:

winget install FiloSottile.mkcert
mkcert -install

Альтернатива через Chocolatey:

choco install mkcert
mkcert -install

После mkcert -install Windows покажет диалог подтверждения — это нормально, так добавляется локальный root CA в доверенные.

Для Linux сценарий зависит от дистрибутива. На Ubuntu/Debian обычно ставят libnss3-tools, чтобы Firefox и Chromium-подобные браузеры корректно увидели CA:

sudo apt update
sudo apt install libnss3-tools

Затем установите бинарник mkcert. Один из вариантов — скачать релиз и положить его в /usr/local/bin:

curl -JLO "https://dl.filippo.io/mkcert/latest?for=linux/amd64"
chmod +x mkcert-v*-linux-amd64
sudo mv mkcert-v*-linux-amd64 /usr/local/bin/mkcert
mkcert -install

Проверьте установку:

mkcert -version
mkcert -CAROOT

mkcert -CAROOT показывает каталог с корневым сертификатом и приватным ключом CA. Обычно туда заходить не нужно, но путь пригодится для диагностики или для редких случаев, когда корневой сертификат нужно вручную импортировать в другой runtime.

Создание сертификата для localhost и dev-доменов

Базовый сертификат для локального сервера создаётся одной командой:

mkcert localhost 127.0.0.1 ::1

В текущей директории появятся два файла: сертификат .pem и приватный ключ -key.pem. Имена могут выглядеть так:

localhost+2.pem
localhost+2-key.pem

Для реального проекта удобнее сразу задать понятные имена и сложить файлы в отдельную папку:

mkdir -p certs
mkcert \
  -cert-file certs/local-dev.pem \
  -key-file certs/local-dev-key.pem \
  localhost 127.0.0.1 ::1 app.test api.app.test admin.app.test

Теперь один сертификат подходит для:

https://localhost
https://app.test
https://api.app.test
https://admin.app.test

Если вы используете wildcard-домены:

mkcert \
  -cert-file certs/app.test.pem \
  -key-file certs/app.test-key.pem \
  "app.test" "*.app.test"

Такой сертификат покроет app.test, api.app.test, admin.app.test, но не покроет deep.api.app.test: wildcard *.app.test работает только на один уровень.

Чтобы локальные домены открывались в браузере, их нужно направить на вашу машину. Для простого случая достаточно /etc/hosts на Linux/macOS или C:\Windows\System32\drivers\etc\hosts на Windows:

127.0.0.1 app.test
127.0.0.1 api.app.test
127.0.0.1 admin.app.test

Для больших команд удобнее локальный DNS: dnsmasq, CoreDNS, встроенный DNS в dev-кластере или wildcard-зона. Но для большинства проектов hosts закрывает задачу.

Не используйте для локальной разработки чужие публичные домены. Лучше выбрать контролируемую зону вроде *.test: домен .test зарезервирован для тестирования и не должен конфликтовать с реальным интернетом.

Подключение сертификата в Node.js, Vite и Nginx

После выпуска сертификата его нужно передать серверу. Ниже несколько типовых вариантов.

Для простого HTTPS-сервера на Node.js:

import https from "node:https";
import fs from "node:fs";
 
const options = {
  cert: fs.readFileSync("./certs/local-dev.pem"),
  key: fs.readFileSync("./certs/local-dev-key.pem"),
};
 
https
  .createServer(options, (req, res) => {
    res.writeHead(200, { "content-type": "text/plain" });
    res.end("Hello over HTTPS\n");
  })
  .listen(3000, () => {
    console.log("https://localhost:3000");
  });

Если вы пишете API без фреймворка, пригодится базовая статья про HTTP-сервер на Go или материал про REST API на Node.js без Express: mkcert добавляется поверх обычного HTTP-сервера, меняется только транспортный слой.

Для Vite конфигурация может выглядеть так:

import { defineConfig } from "vite";
import fs from "node:fs";
 
export default defineConfig({
  server: {
    host: "app.test",
    port: 5173,
    https: {
      cert: fs.readFileSync("./certs/app.test.pem"),
      key: fs.readFileSync("./certs/app.test-key.pem"),
    },
  },
});

Для Nginx локальный reverse proxy удобен, когда нужно собрать несколько сервисов под одним доменом или развести поддомены:

server {
    listen 443 ssl;
    server_name app.test;
 
    ssl_certificate     /etc/nginx/certs/app.test.pem;
    ssl_certificate_key /etc/nginx/certs/app.test-key.pem;
 
    location / {
        proxy_pass http://127.0.0.1:5173;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-Proto https;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}
 
server {
    listen 443 ssl;
    server_name api.app.test;
 
    ssl_certificate     /etc/nginx/certs/app.test.pem;
    ssl_certificate_key /etc/nginx/certs/app.test-key.pem;
 
    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-Proto https;
    }
}

Локальная схема похожа на продакшен-настройку, только сертификат выпущен не Let’s Encrypt, а вашим локальным CA. Для боевого Nginx нужен публичный сертификат; настройка подробно разобрана в материале как настроить HTTPS и бесплатный SSL-сертификат в Nginx.

mkcert и Docker: что учесть

С Docker есть две разные задачи, которые часто смешивают.

Первая: браузер на хосте должен доверять сайту, который отдаёт контейнер. Для этого достаточно установить mkcert на хосте, выпустить сертификат и примонтировать файлы в контейнер с Nginx, Node.js или другим сервером.

Пример для docker compose:

services:
  nginx:
    image: nginx:alpine
    ports:
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
      - ./certs:/etc/nginx/certs:ro

Вторая: процесс внутри контейнера сам ходит по HTTPS к локальному сервису и должен доверять сертификату. Например, контейнер api вызывает https://auth.app.test. Здесь доверие хоста не помогает: у контейнера своё хранилище CA. Нужно добавить корневой сертификат mkcert внутрь образа или примонтировать его и выполнить обновление CA.

Путь к корневому сертификату можно узнать так:

mkcert -CAROOT

В этом каталоге будет файл rootCA.pem. Для Debian/Ubuntu-based образов типовой подход такой:

COPY rootCA.pem /usr/local/share/ca-certificates/mkcert-root-ca.crt
RUN update-ca-certificates

Для Alpine:

RUN apk add --no-cache ca-certificates
COPY rootCA.pem /usr/local/share/ca-certificates/mkcert-root-ca.crt
RUN update-ca-certificates

Не копируйте в образ rootCA-key.pem. Это приватный ключ вашего локального CA. Если он утечёт, любой сможет выпускать сертификаты, которым доверяет ваша машина.

Если Docker-сеть, порты и имена сервисов путаются, стоит отдельно разобрать, как работает Docker networking. Для локального HTTPS часто проблема не в сертификате, а в том, что домен резолвится на хосте, но не резолвится внутри контейнера, или сервис слушает не тот интерфейс.

Безопасность: что можно и нельзя делать

mkcert удобен именно потому, что создаёт доверенный локальный CA. Но это же делает его чувствительным инструментом.

1) Не коммитьте приватные ключи — файлы *-key.pem и особенно rootCA-key.pem не должны попадать в Git. Добавьте локальную папку сертификатов в .gitignore:

certs/
*.key
*-key.pem

Dev-сертификаты можно пересоздать за секунды, поэтому хранить их в репозитории обычно нет смысла.

2) Не используйте mkcert в продакшене — сертификаты mkcert доверены только тем машинам, где установлен ваш локальный CA. Для пользователей они будут недействительными. В продакшене нужны публично доверенные сертификаты: Let’s Encrypt, коммерческий CA, корпоративный CA для внутренних систем.

3) Не рассылайте root CA всей команде без процесса — иногда хочется создать один локальный CA и раздать всем разработчикам. Так можно делать только осознанно: с хранением ключа, отзывом, ротацией и понятным владельцем. Проще и безопаснее, чтобы каждый разработчик выпускал сертификаты своим mkcert.

4) Разделяйте dev и staging — mkcert хорош для ноутбука и локального стенда. Для staging-среды, доступной нескольким людям или CI, лучше использовать нормальную PKI-модель: публичный сертификат, wildcard для тестовой зоны или внутренний корпоративный CA.

5) Удаляйте CA при увольнении или передаче машины — если ноутбук меняет владельца, локальный CA нужно удалить из trust store. Команда mkcert -uninstall убирает CA из хранилищ, а каталог CAROOT можно удалить вручную после проверки.

mkcert не делает TLS «игрушечным». Он просто переносит доверие с публичного CA на локальный. Поэтому правила обращения с приватными ключами остаются теми же.

Типовые ошибки и диагностика

Если браузер всё равно показывает предупреждение, проверьте несколько вещей.

1) Сертификат выпущен не на тот домен — браузер проверяет SAN, а не только CN. Если вы открываете https://api.app.test, домен должен быть в сертификате явно или покрываться wildcard. Проверить содержимое можно так:

openssl x509 -in certs/app.test.pem -noout -text

Ищите блок Subject Alternative Name.

2) CA не установлен в нужное хранилище — выполните:

mkcert -install

На Linux убедитесь, что установлен libnss3-tools, особенно если проблема только в Firefox.

3) Сервер отдаёт другой сертификат — частая ситуация с Nginx, Traefik, Caddy или несколькими virtual hosts. Проверьте, что реально видит клиент:

openssl s_client -connect app.test:443 -servername app.test -showcerts

Разбор этой команды есть в отдельном материале про openssl s_client. Она помогает быстро понять, какой сертификат отдаётся, какая цепочка пришла и совпадает ли SNI.

4) Браузер закэшировал старый сертификат — перезапустите браузер, очистите состояние сайта или откройте в приватном окне. Иногда помогает перезапуск dev-сервера и reverse proxy.

5) Домен не резолвится туда, куда нужно — проверьте /etc/hosts, DNS, VPN и split DNS. Простая команда:

ping app.test

или:

nslookup app.test

Если домен указывает не на 127.0.0.1 или нужный IP локального стенда, TLS до проверки сертификата может даже не дойти.

6) Ошибка смешанного контента — сайт открыт по HTTPS, но грузит скрипты, API или WebSocket по HTTP. В devtools это обычно видно как Mixed Content. Исправляйте URL на https:// и wss://.

Для ошибок уровня браузера вроде ERR_CERT_AUTHORITY_INVALID причина часто именно в недоверенном CA или неверной цепочке. Если похожая проблема возникает уже на публичном сайте, полезен разбор ошибки ERR_CERT_AUTHORITY_INVALID.

Когда mkcert заканчивается и начинается продакшен

Локальный сертификат решает локальную задачу. Перед выпуском в продакшен нужно проверить уже другой набор вещей.

В боевой среде сертификат должен быть выдан публично доверенным CA, корректно установлен на балансировщике или reverse proxy, включать нужные SAN-записи, поддерживать актуальные версии TLS и автоматически обновляться. Для Let’s Encrypt обычно настраивают certbot или встроенную автоматизацию в Caddy/Traefik.

Отдельный риск — срок действия сертификата. Локально вы почти всегда заметите проблему сразу: браузер на вашей машине покажет ошибку. В продакшене просроченный сертификат превращается в простой для пользователей, интеграций и поисковых роботов. Поэтому мониторинг SSL-сертификатов стоит включать отдельно от обычной проверки HTTP-статуса. Подробнее об этом — в статье почему важен мониторинг SSL-сертификатов.

Мониторинг доступности проверяет не только то, что «сервер жив». Например, Statuser может обращаться к сайту с заданным интервалом и присылать уведомление о сбое, если endpoint перестал отвечать или начал возвращать ошибку. Это уже не про mkcert, а про эксплуатацию: локально вы тестируете HTTPS-контур, а в продакшене следите, что он не сломался после деплоя, ротации сертификата или изменения reverse proxy.

Локально используйте mkcert, на staging — максимально похожую на продакшен TLS-схему, в продакшене — публичные сертификаты, автоматическое продление и внешний мониторинг. Тогда разрыв между «у меня работает» и «у пользователей открывается без ошибок» становится заметно меньше.

FAQ

Можно ли использовать mkcert для публичного сайта?

Нет. Сертификаты mkcert доверены только машинам, где установлен ваш локальный CA. Для публичного сайта нужен сертификат от публично доверенного центра сертификации.

Чем mkcert лучше обычного self-signed сертификата?

mkcert создаёт локальный CA и добавляет его в доверенные хранилища. Поэтому браузер доверяет выпущенным сертификатам без ручных исключений и предупреждений.

Нужно ли ставить mkcert всем разработчикам в команде?

Обычно да. Каждый разработчик может создать свой локальный CA и выпустить сертификаты для одинаковых dev-доменов. Это безопаснее, чем раздавать один общий приватный ключ CA.

Почему сертификат работает в Chrome, но не работает в Firefox?

Firefox может использовать отдельное NSS-хранилище. На Linux установите libnss3-tools и повторите mkcert -install. Иногда нужно перезапустить браузер.

Опубликовано 6 ноября 202610 минут чтенияГригорий Чалый
Средний рейтинг статьи — 4.8

Настроить мониторинг за 30 секунд

Надёжные уведомления о даунтаймах. Без ложных срабатываний