Dos herramientas, un problema: tu servidor de desarrollo necesita ser alcanzable por HTTPS. mkcert te da un certificado TLS confiable localmente para que https://localhost:3000 simplemente funcione. Un túnel te da una URL HTTPS pública respaldada por un certificado real de una CA real. No son competidores — resuelven problemas distintos —, pero la gente sigue tratándolos como si lo fueran.
La respuesta corta
- ¿Necesitas HTTPS solo en tu propia máquina, para tu propio navegador? Usa mkcert.
- ¿Necesitas que un servicio externo (Stripe, GitHub, Auth0, un teléfono) alcance tu servidor de desarrollo? Usa un túnel.
- ¿Ambos? Corre ambos. No entran en conflicto.
Ese es el artículo entero en tres viñetas. El resto es solo el porqué.
mkcert: certificados confiables localmente en 30 segundos
mkcert instala una autoridad de certificación local en el almacén de confianza de tu sistema operativo y emite certificados desde ella. Tu navegador ve el certificado como confiable porque la CA lo es. Sin avisos de tu conexión no es privada, sin flags --ignore-certificate-errors, sin armar tus propios certificados autofirmados.
# una vez por máquina
mkcert -install
# una vez por proyecto
mkcert localhost 127.0.0.1
# ahora tienes localhost.pem y localhost-key.pem
Conéctalos a tu servidor de desarrollo (Vite, Next.js, Express, Django runserver_plus, todos soportan args de TLS) y tienes https://localhost:3000 con un certificado real.
El truco: solo tu máquina confía en esa CA. El navegador de tu compañero avisará. Tu teléfono no confiará sin instalar manualmente la raíz de la CA.
Túnel: HTTPS real, URL pública real
Un túnel localhost reenvía tráfico desde una URL HTTPS pública a tu puerto local. El certificado lo emite una CA real (Let's Encrypt o similar) y lo confía cada navegador en cada dispositivo.
npx portpreview 3000
El túnel gestiona la terminación TLS en la pasarela de la nube. Tu servidor local puede quedarse en HTTP plano — la URL pública es HTTPS, y esa es la superficie que toca cada servicio externo.
Lado a lado
| Necesidad | mkcert | Túnel |
|---|---|---|
| HTTPS en tu navegador local | Sí | Sí (vía la URL pública) |
| Pruebas de service worker / cookie segura en tu navegador | Sí | Sí |
| Un proveedor externo puede alcanzarte | No | Sí |
| El teléfono en tu mano puede alcanzarte | No (sin instalar la CA) | Sí |
| El proveedor OAuth acepta la URL | A veces (Google: sí; muchos: no) | Sí |
| Stripe / GitHub / Twilio pueden hacer webhook | No | Sí |
| Tiempo de configuración | 30 segundos por máquina | Un comando por sesión |
| Funciona sin conexión | Sí | No |
| Sobrevive al modo avión | Sí | No |
Donde la gente se equivoca
Intentar usar mkcert para pruebas de webhook
Stripe no puede confiar en la CA local de tu máquina. No importa cuán confiable se vea el certificado en tu navegador — Stripe está en una red distinta. Necesitas un túnel para cualquier tráfico entrante de la internet pública.
Usar un túnel para HTTPS en solitario solo de navegador
Si solo quieres que funcionen los service workers o que se ponga una cookie segura en tu propio navegador, mkcert es más rápido y funciona sin conexión. No quemes una sesión de túnel por lo que resuelve un archivo de certificado.
Elegir una herramienta y forzar por ella el otro caso de uso
Correr ambas está bien. La mayor parte de nuestra configuración usa mkcert para el trabajo diario del lado del navegador y un túnel cuando probamos webhooks o callbacks OAuth. Cohabitan en package.json sin conflicto.
¿Y Caddy o nginx?
Sí, puedes correr Caddy con HTTPS automático delante de tu servidor de desarrollo, y eso también funciona — es esencialmente «mkcert con pasos extra y un proxy inverso». Para la mayoría del desarrollo local, mkcert es más simple. Para enrutamiento más elaborado con varios servicios locales, Caddy se gana su lugar.
Nuestra configuración real
Un repo en el que trabajamos tiene mkcert en el script de desarrollo para HTTPS del lado localhost, y un script npm tunnel aparte que corre portpreview cuando alguien necesita webhooks o pruebas en móvil. La URL del túnel se pasa por variable de entorno para que las redirect URIs de OAuth sean fáciles de intercambiar. Tomó 20 minutos conectarlo y nunca volvimos a pensar en TLS local.
Para configuración de túnel específica de OAuth, mira cómo probar callbacks OAuth en local. Para compartir previews con compañeros o diseñadores, compartir tu servidor de desarrollo local. Únete a la lista de espera de PortPreview para el lado del túnel de esta configuración.