mkcert: локальные HTTPS-сертификаты для разработки
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 -CAROOTmkcert -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.pemDev-сертификаты можно пересоздать за секунды, поэтому хранить их в репозитории обычно нет смысла.
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. Иногда нужно перезапустить браузер.
Похожие статьи

Как настроить reverse proxy для WebSocket в Nginx
Подробный разбор настройки WebSocket через Nginx: минимальная конфигурация, HTTPS (wss), таймауты, отключение буферизации и отладка проблем.
19 января 20269 мин

Что такое прокси-сервер и как он работает
Пошагово объясняем, как работает прокси-сервер, в чем разница между HTTP, HTTPS и SOCKS5, когда использовать reverse proxy и как запустить Squid на Ubuntu.
26 марта 20268 мин

Создание самоподписанного SSL-сертификата с помощью OpenSSL
Научитесь создавать самоподписанные SSL-сертификаты для разработки и внутреннего использования. Команды OpenSSL, советы, форматы и проверка.
20 апреля 20256 мин
Настроить мониторинг за 30 секунд
Надёжные уведомления о даунтаймах. Без ложных срабатываний