Hai công cụ, một vấn đề: máy chủ dev của bạn cần tiếp cận được qua HTTPS. mkcert cho bạn một chứng chỉ TLS được tin cậy cục bộ để https://localhost:3000 chạy ngay. Một tunnel cho bạn một URL HTTPS công khai được hậu thuẫn bởi chứng chỉ thật từ một CA thật. Chúng không phải đối thủ — giải quyết các vấn đề khác nhau — nhưng người ta cứ coi như vậy.
Câu trả lời ngắn
- Chỉ cần HTTPS trên máy của bạn, cho trình duyệt của bạn? Dùng mkcert.
- Cần một dịch vụ bên ngoài (Stripe, GitHub, Auth0, một điện thoại) tiếp cận máy chủ dev? Dùng tunnel.
- Cả hai? Chạy cả hai. Chúng không xung đột.
Đó là cả bài viết trong ba gạch đầu dòng. Phần còn lại chỉ là lý do.
mkcert: chứng chỉ tin cậy cục bộ trong 30 giây
mkcert cài một tổ chức cấp chứng chỉ cục bộ vào kho tin cậy của hệ điều hành và phát chứng chỉ từ đó. Trình duyệt thấy chứng chỉ là đáng tin vì CA đáng tin. Không cảnh báo kết nối của bạn không riêng tư, không cờ --ignore-certificate-errors, không tự tay tạo chứng chỉ tự ký.
# một lần mỗi máy
mkcert -install
# một lần mỗi dự án
mkcert localhost 127.0.0.1
# giờ bạn có localhost.pem và localhost-key.pem
Gắn chúng vào máy chủ dev (Vite, Next.js, Express, Django runserver_plus đều hỗ trợ tham số TLS) và bạn có https://localhost:3000 với chứng chỉ thật.
Điểm vướng: chỉ máy của bạn tin CA đó. Trình duyệt của đồng nghiệp sẽ cảnh báo. Điện thoại của bạn sẽ không tin nếu không tự cài gốc CA.
Tunnel: HTTPS thật, URL công khai thật
Một tunnel localhost chuyển tiếp lưu lượng từ một URL HTTPS công khai tới cổng cục bộ của bạn. Chứng chỉ do một CA thật (Let's Encrypt hoặc tương tự) phát và mọi trình duyệt trên mọi thiết bị đều tin.
npx portpreview 3000
Tunnel xử lý kết thúc TLS tại cổng gateway đám mây. Máy chủ cục bộ của bạn có thể ở HTTP trơn — URL công khai là HTTPS, và đó là bề mặt mọi dịch vụ bên ngoài chạm tới.
Đặt cạnh nhau
| Nhu cầu | mkcert | Tunnel |
|---|---|---|
| HTTPS trong trình duyệt cục bộ | Có | Có (qua URL công khai) |
| Kiểm thử service worker / cookie an toàn trong trình duyệt | Có | Có |
| Nhà cung cấp bên ngoài tiếp cận được bạn | Không | Có |
| Điện thoại trên tay tiếp cận được bạn | Không (nếu không cài CA) | Có |
| Nhà cung cấp OAuth chấp nhận URL | Đôi khi (Google: có; nhiều: không) | Có |
| Stripe / GitHub / Twilio gửi webhook được | Không | Có |
| Thời gian thiết lập | 30 giây mỗi máy | Một lệnh mỗi phiên |
| Hoạt động offline | Có | Không |
| Sống sót chế độ máy bay | Có | Không |
Nơi người ta hiểu sai
Cố dùng mkcert để kiểm thử webhook
Stripe không thể tin CA cục bộ của máy bạn. Dù chứng chỉ trông đáng tin đến đâu trong trình duyệt — Stripe ở mạng khác. Bạn cần một tunnel cho bất kỳ lưu lượng đến nào từ internet công khai.
Dùng tunnel cho HTTPS một mình chỉ trong trình duyệt
Nếu bạn chỉ muốn service worker chạy hoặc đặt một cookie an toàn trong trình duyệt của chính mình, mkcert nhanh hơn và chạy offline. Đừng đốt một phiên tunnel cho thứ mà một file chứng chỉ giải quyết.
Chọn một công cụ và ép trường hợp kia qua nó
Chạy cả hai là ổn. Phần lớn thiết lập của chúng tôi dùng mkcert cho công việc phía trình duyệt hằng ngày và tunnel khi kiểm thử webhook hoặc callback OAuth. Chúng sống chung trong package.json không xung đột.
Còn Caddy hay nginx thì sao?
Có, bạn có thể chạy Caddy với HTTPS tự động trước máy chủ dev, và điều đó cũng chạy — về cơ bản là "mkcert với vài bước thêm và một reverse proxy". Với phần lớn dev cục bộ, mkcert đơn giản hơn. Với định tuyến phức tạp hơn cùng nhiều dịch vụ cục bộ, Caddy đáng giá.
Thiết lập thực tế của chúng tôi
Một repo chúng tôi làm việc có mkcert trong script dev cho HTTPS phía localhost, và một script npm tunnel riêng chạy portpreview khi ai đó cần webhook hoặc kiểm thử di động. URL tunnel được truyền qua biến môi trường nên redirect URI của OAuth dễ thay. Mất 20 phút để đấu nối và từ đó không bao giờ phải nghĩ về TLS cục bộ nữa.
Về thiết lập tunnel riêng cho OAuth, xem cách kiểm thử callback OAuth cục bộ. Để chia sẻ bản xem trước với đồng nghiệp hay nhà thiết kế, chia sẻ máy chủ dev cục bộ. Tham gia danh sách chờ của PortPreview để có phần tunnel của thiết lập này.