Como configurar HTTPS local com mkcert

1. Por que HTTPS local é essencial no desenvolvimento moderno

1.1. Diferença entre HTTP e HTTPS no ambiente de desenvolvimento

No desenvolvimento web moderno, HTTPS deixou de ser opcional para se tornar obrigatório, mesmo em ambientes locais. Enquanto HTTP transmite dados em texto puro, HTTPS utiliza criptografia TLS/SSL para proteger a comunicação entre cliente e servidor. Essa diferença se torna crítica quando você precisa testar funcionalidades que dependem de conexões seguras.

1.2. Problemas comuns ao testar APIs, cookies seguros e service workers sem HTTPS

Desenvolver sem HTTPS local pode gerar diversos problemas:

  • Cookies seguros (Secure flag): navegadores bloqueiam cookies marcados como Secure em conexões HTTP
  • Service Workers: exigem HTTPS para funcionar, mesmo em desenvolvimento
  • APIs de geolocalização e notificações: muitas APIs modernas do navegador requerem contexto seguro
  • Mixed content: recursos carregados via HTTP em páginas HTTPS são bloqueados
  • CORS e autenticação: fluxos de autenticação OAuth e tokens JWT podem falhar

1.3. Por que certificados autoassinados tradicionais falham

Certificados autoassinados tradicionais geram avisos de segurança no navegador porque não são reconhecidos como confiáveis pelo sistema operacional. O desenvolvedor precisa manualmente adicionar exceções de segurança, o que torna o fluxo de trabalho lento e inseguro.

2. O que é o mkcert e como ele resolve o problema

2.1. Visão geral da ferramenta

mkcert é uma ferramenta open-source que cria certificados TLS/SSL confiáveis localmente. Desenvolvida pelo time do Filippo Valsorda (ex-Go team), ela simplifica drasticamente a configuração de HTTPS em ambientes de desenvolvimento.

2.2. Como o mkcert integra uma CA local ao sistema

O mkcert funciona em dois passos principais:

  1. Cria uma Autoridade Certificadora (CA) local no seu sistema
  2. Instala essa CA como confiável nos stores de certificados do sistema operacional e navegadores

Isso significa que qualquer certificado assinado por essa CA será automaticamente confiável, eliminando os avisos de segurança.

2.3. Comparação com alternativas

Ferramenta Vantagens Desvantagens
mkcert Simples, automático, multiplataforma Apenas para desenvolvimento
OpenSSL Flexível, controle total Complexo, comandos longos
Let's Encrypt Certificados reais Requer domínio público, porta 80
Caddy Automático, integrado Depende do servidor Caddy

3. Instalação do mkcert nos principais sistemas operacionais

3.1. Instalação no Linux

# Ubuntu/Debian
sudo apt install libnss3-tools
wget https://github.com/FiloSottile/mkcert/releases/latest/download/mkcert-v1.4.4-linux-amd64
chmod +x mkcert-v1.4.4-linux-amd64
sudo mv mkcert-v1.4.4-linux-amd64 /usr/local/bin/mkcert

# Fedora/CentOS
sudo dnf install nss-tools
# Seguir o mesmo processo de download

# Arch Linux
sudo pacman -S mkcert

3.2. Instalação no macOS

# Usando Homebrew
brew install mkcert
brew install nss  # Para suporte a Firefox

3.3. Instalação no Windows

# Usando Chocolatey
choco install mkcert

# Ou download manual do binário
# Acesse: https://github.com/FiloSottile/mkcert/releases
# Baixe mkcert-v1.4.4-windows-amd64.exe
# Renomeie para mkcert.exe e adicione ao PATH

4. Configuração inicial: criando a Autoridade Certificadora local

4.1. Comando mkcert -install

# Execute no terminal
mkcert -install

# Saída esperada:
# Created a new local CA at "C:\Users\seuuser\AppData\Local\mkcert" 
# The local CA is now installed in the system trust store!
# The local CA is now installed in the Firefox trust store!

Nos bastidores, o mkcert:
1. Gera uma chave privada para a CA
2. Cria um certificado autoassinado para a CA
3. Instala o certificado nos trust stores do sistema e navegadores

4.2. Verificando a CA instalada

# No navegador Chrome/Edge
# Acesse chrome://settings/certificates
# Procure por "mkcert" na aba "Autoridades de Certificação"

# No Firefox
# Acesse about:preferences#privacy
# Role até "Certificados" e clique em "Ver Certificados"

# No terminal (Linux/macOS)
mkcert -CAROOT
# Mostra o diretório onde a CA está armazenada

4.3. Solução de problemas comuns

# Problema: "permission denied" no Linux
sudo mkcert -install

# Problema: Firefox não reconhece a CA
mkcert -install  # O Firefox usa seu próprio trust store

# Problema: Antivírus bloqueando
# Adicione exceção para o processo mkcert no seu antivírus

5. Gerando certificados para desenvolvimento local

5.1. Sintaxe básica

# Certificado para localhost e IP local
mkcert localhost 127.0.0.1 ::1

# Saída esperada:
# 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"

5.2. Gerando para domínios personalizados

# Domínio personalizado para desenvolvimento
mkcert meusite.test "*.meusite.test"

# Múltiplos domínios e IPs
mkcert localhost 127.0.0.1 meusite.test api.meusite.test

5.3. Entendendo os arquivos gerados

# Estrutura dos arquivos
localhost+2.pem       # Certificado (público)
localhost+2-key.pem   # Chave privada (NUNCA compartilhar)

# Para usar em servidores, você precisa de ambos
# O certificado contém:
# - Nome do domínio
# - Chave pública
# - Assinatura da CA local
# - Período de validade (2 anos por padrão)

6. Integrando o certificado mkcert em servidores locais

6.1. Configuração em Node.js

// server.js
const https = require('https');
const fs = require('fs');
const express = require('express');

const app = express();
const options = {
  key: fs.readFileSync('localhost+2-key.pem'),
  cert: fs.readFileSync('localhost+2.pem')
};

https.createServer(options, app).listen(3000, () => {
  console.log('Servidor HTTPS rodando em https://localhost:3000');
});

6.2. Configuração em Python

# server.py (Flask)
from flask import Flask
import ssl

app = Flask(__name__)

if __name__ == '__main__':
    context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
    context.load_cert_chain('localhost+2.pem', 'localhost+2-key.pem')
    app.run(ssl_context=context, host='0.0.0.0', port=5000)

# Alternativa com http.server
python -c "
import http.server, ssl
server = http.server.HTTPServer(('localhost', 4443), http.server.SimpleHTTPRequestHandler)
server.socket = ssl.wrap_socket(server.socket, certfile='localhost+2.pem', keyfile='localhost+2-key.pem', server_side=True)
server.serve_forever()
"

6.3. Configuração em PHP

# PHP built-in server com SSL não suporta nativamente
# Use Nginx como proxy reverso

# Configuração Nginx
server {
    listen 443 ssl;
    server_name meusite.test;

    ssl_certificate /caminho/localhost+2.pem;
    ssl_certificate_key /caminho/localhost+2-key.pem;

    root /var/www/html;
    index index.php;

    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}

7. Boas práticas e manutenção dos certificados locais

7.1. Expiração e renovação

# Certificados mkcert expiram em 2 anos
# Para renovar, basta gerar novamente
mkcert -install  # Se necessário
mkcert localhost 127.0.0.1  # Gera novos certificados

# Verificar validade
openssl x509 -in localhost+2.pem -text -noout | grep -A2 "Validity"

7.2. Organização em projetos com Docker

# Estrutura recomendada
projeto/
├── docker/
│   ├── nginx/
│   │   └── default.conf
│   └── certs/
│       ├── localhost+2.pem
│       └── localhost+2-key.pem
├── docker-compose.yml
└── src/

# No docker-compose.yml
services:
  web:
    image: nginx:alpine
    volumes:
      - ./docker/certs:/etc/nginx/certs
      - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf
    ports:
      - "443:443"

7.3. Segurança: nunca versionar certificados

# Adicione ao .gitignore
*.pem
*.key
certs/

# Ou use variáveis de ambiente
# .env
SSL_CERT_PATH=./certs/localhost.pem
SSL_KEY_PATH=./certs/localhost-key.pem

8. Depuração e verificação da configuração HTTPS

8.1. Testando com curl

# Testar conexão HTTPS local
curl -v https://localhost:3000

# Verificar detalhes do certificado
curl --cacert ~/.local/share/mkcert/rootCA.pem https://localhost:3000

# Testar com verbose para ver handshake TLS
curl -v --insecure https://localhost:3000 2>&1 | grep -i "SSL\|certificate"

8.2. Identificando erros comuns

# Erro: "ERR_CERT_AUTHORITY_INVALID"
# Solução: Reinstalar a CA
mkcert -uninstall && mkcert -install

# Erro: "ERR_CERT_COMMON_NAME_INVALID"
# Solução: Gerar certificado com o domínio correto
mkcert seudominio.test

# Erro: "ERR_SSL_PROTOCOL_ERROR"
# Solução: Verificar se o servidor está na porta correta
# e se os arquivos .pem estão no local esperado

8.3. Checklist final de validação

Checklist de configuração HTTPS local:
[ ] mkcert -install executado com sucesso
[ ] Certificados gerados para os domínios corretos
[ ] Servidor configurado com caminhos absolutos para .pem
[ ] Porta 443 ou porta personalizada configurada
[ ] Navegador acessa https://localhost sem avisos
[ ] Cookies Secure funcionando
[ ] Service Workers registrados sem erros
[ ] Mixed content não reportado
[ ] Redirecionamentos HTTP → HTTPS funcionando

Referências