Zwei Tools, ein Problem: Dein Dev-Server muss über HTTPS erreichbar sein. mkcert gibt dir ein lokal vertrautes TLS-Zertifikat, sodass https://localhost:3000 einfach funktioniert. Ein Tunnel gibt dir eine öffentliche HTTPS-URL mit einem echten Zertifikat von einer echten CA. Sie sind keine Konkurrenten — sie lösen verschiedene Probleme —, aber die Leute behandeln sie ständig so.
Die kurze Antwort
- Du brauchst HTTPS nur auf deiner eigenen Maschine, für deinen eigenen Browser? Nimm mkcert.
- Du brauchst, dass ein externer Dienst (Stripe, GitHub, Auth0, ein Telefon) deinen Dev-Server erreicht? Nimm einen Tunnel.
- Beides? Fahre beides. Sie stehen sich nicht im Weg.
Das ist der ganze Artikel in drei Punkten. Der Rest ist nur das Warum.
mkcert: lokal vertraute Zertifikate in 30 Sekunden
mkcert installiert eine lokale Zertifizierungsstelle in den Trust Store deines Betriebssystems und stellt daraus Zertifikate aus. Dein Browser sieht das Zertifikat als vertraut, weil die CA vertraut ist. Keine Diese Verbindung ist nicht privat-Warnungen, keine --ignore-certificate-errors-Flags, kein Selbstbasteln eigener self-signed Certs.
# einmal pro Maschine
mkcert -install
# einmal pro Projekt
mkcert localhost 127.0.0.1
# du hast jetzt localhost.pem und localhost-key.pem
Binde sie in deinen Dev-Server ein (Vite, Next.js, Express, Django runserver_plus unterstützen alle TLS-Args), und du hast https://localhost:3000 mit einem echten Zertifikat.
Der Haken: nur deine Maschine vertraut dieser CA. Der Browser deines Teamkollegen warnt. Dein Telefon vertraut ihr nicht, ohne dass du das CA-Root manuell installierst.
Tunnel: echtes HTTPS, echte öffentliche URL
Ein localhost-Tunnel leitet Traffic von einer öffentlichen HTTPS-URL zu deinem lokalen Port. Das Zertifikat wird von einer echten CA (Let's Encrypt o. Ä.) ausgestellt und von jedem Browser auf jedem Gerät vertraut.
npx portpreview 3000
Der Tunnel übernimmt die TLS-Terminierung am Cloud-Gateway. Dein lokaler Server kann auf reinem HTTP bleiben — die öffentliche URL ist HTTPS, und das ist die Fläche, die jeder externe Dienst berührt.
Seite an Seite
| Bedarf | mkcert | Tunnel |
|---|---|---|
| HTTPS in deinem lokalen Browser | Ja | Ja (über die öffentliche URL) |
| Service-Worker- / Secure-Cookie-Test im Browser | Ja | Ja |
| Externer Provider kann dich erreichen | Nein | Ja |
| Telefon in der Hand kann dich erreichen | Nein (ohne CA-Installation) | Ja |
| OAuth-Provider akzeptiert die URL | Manchmal (Google: ja; viele: nein) | Ja |
| Stripe / GitHub / Twilio können webhooken | Nein | Ja |
| Setup-Zeit | 30 Sekunden pro Maschine | Ein Befehl pro Session |
| Funktioniert offline | Ja | Nein |
| Überlebt Flugmodus | Ja | Nein |
Wo die Leute das falsch machen
mkcert für Webhook-Testing nutzen wollen
Stripe kann der lokalen CA deiner Maschine nicht vertrauen. Egal wie vertraut das Zertifikat in deinem Browser aussieht — Stripe ist in einem anderen Netzwerk. Für jeden eingehenden Traffic aus dem öffentlichen Internet brauchst du einen Tunnel.
Einen Tunnel für reines Solo-Browser-HTTPS nutzen
Willst du nur, dass Service Worker funktionieren oder ein Secure Cookie in deinem eigenen Browser gesetzt wird, ist mkcert schneller und offline-tauglich. Verbrenne keine Tunnel-Session für etwas, das eine Zertifikatsdatei löst.
Ein Tool wählen und den anderen Use-Case durchzwingen
Beides zu fahren ist okay. Der Großteil unseres Setups nutzt mkcert für die tägliche Browser-Arbeit und einen Tunnel, wenn wir Webhooks oder OAuth-Callbacks testen. Sie wohnen ohne Konflikt in package.json zusammen.
Was ist mit Caddy oder nginx?
Ja, du kannst Caddy mit automatischem HTTPS vor deinem Dev-Server fahren, und das funktioniert auch — es ist im Grunde „mkcert mit Extraschritten und einem Reverse Proxy". Für die meiste lokale Entwicklung ist mkcert einfacher. Für aufwendigeres Routing mit mehreren lokalen Diensten verdient sich Caddy sein Geld.
Unser tatsächliches Setup
Ein Repo, in dem wir arbeiten, hat mkcert im Dev-Skript für localhost-seitiges HTTPS und ein separates tunnel-npm-Skript, das portpreview startet, wenn jemand Webhooks oder Mobile-Testing braucht. Die Tunnel-URL kommt per Env-Var rein, sodass OAuth-Redirect-URIs leicht zu tauschen sind. Es dauerte 20 Minuten zu verkabeln, und wir haben seither nie wieder über lokales TLS nachgedacht.
Für OAuth-spezifisches Tunnel-Setup siehe wie man OAuth-Callbacks lokal testet. Zum Teilen von Previews mit Teamkollegen oder Designern deinen lokalen Dev-Server teilen. Tritt der PortPreview-Warteliste bei für die Tunnel-Seite dieses Setups.