Duas ferramentas, um problema: seu servidor de desenvolvimento precisa ser alcançável por HTTPS. O mkcert te dá um certificado TLS confiável localmente para que https://localhost:3000 simplesmente funcione. Um túnel te dá uma URL HTTPS pública respaldada por um certificado real de uma CA real. Eles não são concorrentes — resolvem problemas diferentes —, mas as pessoas insistem em tratá-los como se fossem.
A resposta curta
- Precisa de HTTPS só na sua própria máquina, para o seu próprio navegador? Use mkcert.
- Precisa que um serviço externo (Stripe, GitHub, Auth0, um celular) alcance seu servidor de desenvolvimento? Use um túnel.
- Ambos? Rode ambos. Eles não entram em conflito.
Esse é o artigo inteiro em três tópicos. O resto é só o porquê.
mkcert: certificados confiáveis localmente em 30 segundos
O mkcert instala uma autoridade certificadora local no armazenamento de confiança do seu sistema operacional e emite certificados a partir dela. Seu navegador vê o certificado como confiável porque a CA é confiável. Sem avisos de sua conexão não é particular, sem flags --ignore-certificate-errors, sem montar seus próprios certificados autoassinados.
# uma vez por máquina
mkcert -install
# uma vez por projeto
mkcert localhost 127.0.0.1
# agora você tem localhost.pem e localhost-key.pem
Conecte-os ao seu servidor de desenvolvimento (Vite, Next.js, Express, Django runserver_plus, todos suportam args de TLS) e você tem https://localhost:3000 com um certificado real.
O porém: só a sua máquina confia naquela CA. O navegador do seu colega vai avisar. Seu celular não vai confiar sem instalar manualmente a raiz da CA.
Túnel: HTTPS real, URL pública real
Um túnel localhost encaminha tráfego de uma URL HTTPS pública para a sua porta local. O certificado é emitido por uma CA real (Let's Encrypt ou similar) e confiado por cada navegador em cada dispositivo.
npx portpreview 3000
O túnel cuida da terminação TLS no gateway da nuvem. Seu servidor local pode ficar em HTTP puro — a URL pública é HTTPS, e essa é a superfície que cada serviço externo toca.
Lado a lado
| Necessidade | mkcert | Túnel |
|---|---|---|
| HTTPS no seu navegador local | Sim | Sim (via a URL pública) |
| Testes de service worker / cookie seguro no navegador | Sim | Sim |
| Provedor externo consegue te alcançar | Não | Sim |
| Celular na sua mão consegue te alcançar | Não (sem instalar a CA) | Sim |
| Provedor OAuth aceita a URL | Às vezes (Google: sim; muitos: não) | Sim |
| Stripe / GitHub / Twilio podem fazer webhook | Não | Sim |
| Tempo de configuração | 30 segundos por máquina | Um comando por sessão |
| Funciona offline | Sim | Não |
| Sobrevive ao modo avião | Sim | Não |
Onde as pessoas erram
Tentar usar mkcert para testes de webhook
O Stripe não consegue confiar na CA local da sua máquina. Não importa quão confiável o certificado pareça no seu navegador — o Stripe está em outra rede. Você precisa de um túnel para qualquer tráfego de entrada da internet pública.
Usar um túnel para HTTPS solo só de navegador
Se você só quer que service workers funcionem ou que um cookie seguro seja definido no seu próprio navegador, o mkcert é mais rápido e funciona offline. Não queime uma sessão de túnel pelo que um arquivo de certificado resolve.
Escolher uma ferramenta e forçar o outro caso de uso por ela
Rodar ambos é tranquilo. A maior parte da nossa configuração usa mkcert para o trabalho diário do lado do navegador e um túnel quando testamos webhooks ou callbacks OAuth. Eles coabitam no package.json sem conflito.
E o Caddy ou o nginx?
Sim, você pode rodar o Caddy com HTTPS automático na frente do seu servidor de desenvolvimento, e isso também funciona — é essencialmente "mkcert com passos extras e um proxy reverso". Para a maioria do desenvolvimento local, o mkcert é mais simples. Para roteamento mais elaborado com vários serviços locais, o Caddy compensa.
Nossa configuração de verdade
Um repo em que trabalhamos tem mkcert no script de dev para HTTPS do lado localhost, e um script npm tunnel à parte que roda portpreview quando alguém precisa de webhooks ou testes em mobile. A URL do túnel é passada por variável de ambiente para que as redirect URIs do OAuth sejam fáceis de trocar. Levou 20 minutos para montar e nunca mais pensamos em TLS local.
Para configuração de túnel específica de OAuth, veja como testar callbacks OAuth em local. Para compartilhar previews com colegas ou designers, compartilhar seu servidor de desenvolvimento local. Entre na lista de espera do PortPreview para o lado do túnel dessa configuração.