Browser kamu nolak cookie, service worker nggak mau load, atau API third-party complain karena request datang dari http://localhost—semua itu ujungnya satu masalah: kamu develop di HTTP sementara production jalan di HTTPS.
Ini bukan edge case. Kalau kamu pakai fitur seperti Secure cookie, WebAuthn, Payment Request API, atau geolocation, browser modern mengharuskan HTTPS bahkan di lokal. Dan kalau backend kamu ada di balik reverse proxy yang terminate SSL, behavior-nya di lokal bakal beda banget sama production.
Artikel ini bahas local HTTPS development setup dari nol: bikin CA sendiri, generate cert, sampai trust cert itu di browser dan sistem operasi—tanpa bayar, tanpa expose port ke internet.
Kenapa localhost HTTP Makin Sering Bikin Masalah
Dulu http://localhost cukup untuk develop. Sekarang tidak. Beberapa hal yang rusak kalau kamu tetap pakai HTTP lokal:
SameSite=None; Securecookie — wajib HTTPS, kalau tidak cookie diabaikan browser.- Service Worker — hanya jalan di HTTPS atau
localhost, tapi subdomain lokal sepertiapp.localbutuh HTTPS. - Mixed content blocking — frontend HTTPS yang fetch ke backend HTTP lokal langsung diblokir.
- OAuth redirect — beberapa provider nolak redirect URI yang bukan HTTPS.
- HTTP/2 — browser hanya support HTTP/2 lewat TLS.
Jadi bukan paranoia, ini kebutuhan teknis yang nyata.
Pilihan Tool: mkcert
Ada beberapa cara setup local HTTPS: OpenSSL manual, self-signed cert, atau pakai tool seperti mkcert. Gue rekomendasikan mkcert karena satu alasan sederhana: dia otomatis install Certificate Authority (CA) ke trust store sistem operasi dan browser kamu. Jadi nggak ada warning "Your connection is not private" yang bikin pusing.
Install mkcert
macOS (Homebrew):
brew install mkcert
brew install nss # kalau pakai Firefox
Linux (Debian/Ubuntu):
sudo apt install libnss3-tools
# Download binary terbaru dari GitHub
curl -Lo mkcert https://github.com/FiloSottile/mkcert/releases/latest/download/mkcert-v1.4.4-linux-amd64
chmod +x mkcert
sudo mv mkcert /usr/local/bin/
Windows (Chocolatey):
choco install mkcert
Install Local CA
mkcert -install
Perintah ini bikin local CA dan install ke:
- System trust store (macOS Keychain / Linux NSS / Windows Certificate Store)
- Firefox NSS database (kalau
nssterinstall)
Output-nya bakal kayak gini:
Created a new local CA 💥
The local CA is now installed in the system trust store! ⚡️
The local CA is now installed in the Firefox and/or Chrome/Chromium trust store (requires browser restart)! 🦊
Generate Certificate untuk Domain Lokal
Sekarang generate cert untuk domain yang kamu pakai:
mkcert localhost 127.0.0.1 ::1 app.local *.app.local
Ini generate dua file:
localhost+4.pem— certificatelocalhost+4-key.pem— private key
Gue biasanya simpan di folder dedicated supaya rapi:
mkdir -p ~/.local/certs
cd ~/.local/certs
mkcert localhost 127.0.0.1 ::1 app.local *.app.local
Setup di Berbagai Stack
Node.js / Express
// server.js
const https = require('https');
const fs = require('fs');
const express = require('express');
const path = require('path');
const app = express();
const CERTS_DIR = path.join(process.env.HOME, '.local/certs');
app.get('/', (req, res) => {
res.send('HTTPS lokal jalan!');
});
const options = {
key: fs.readFileSync(path.join(CERTS_DIR, 'localhost+4-key.pem')),
cert: fs.readFileSync(path.join(CERTS_DIR, 'localhost+4.pem')),
};
https.createServer(options, app).listen(3443, () => {
console.log('Server HTTPS jalan di https://localhost:3443');
});
Jalankan:
node server.js
Buka https://localhost:3443 di browser—harusnya langsung hijau, tanpa warning.
Vite (React / Vue / Svelte)
Vite punya plugin resmi untuk ini:
npm install -D @vitejs/plugin-basic-ssl
# atau pakai mkcert plugin yang lebih proper:
npm install -D vite-plugin-mkcert
// vite.config.js
import { defineConfig } from 'vite';
import mkcert from 'vite-plugin-mkcert';
export default defineConfig({
plugins: [mkcert()],
server: {
https: true,
port: 5173,
},
});
Plugin ini otomatis handle cert generation dan trust. Jalankan npm run dev dan Vite langsung serve di HTTPS.
Nginx sebagai Reverse Proxy Lokal
Kalau kamu punya beberapa service dan mau akses lewat subdomain (misalnya api.app.local, app.app.local), Nginx adalah pilihan yang clean:
Generate cert dulu:
cd ~/.local/certs
mkcert app.local "*.app.local"
Konfigurasi Nginx:
# /etc/nginx/sites-available/app.local
server {
listen 443 ssl;
server_name app.local;
ssl_certificate /home/kamu/.local/certs/app.local+1.pem;
ssl_certificate_key /home/kamu/.local/certs/app.local+1-key.pem;
location / {
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
server {
listen 443 ssl;
server_name api.app.local;
ssl_certificate /home/kamu/.local/certs/app.local+1.pem;
ssl_certificate_key /home/kamu/.local/certs/app.local+1-key.pem;
location / {
proxy_pass http://localhost:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Aktifkan dan reload:
sudo ln -s /etc/nginx/sites-available/app.local /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
Tambah ke /etc/hosts:
127.0.0.1 app.local
127.0.0.1 api.app.local
Sekarang https://app.local dan https://api.app.local bisa diakses langsung dari browser.
Docker Compose Setup
Kalau kamu develop pakai Docker, mount cert ke container:
# docker-compose.yml
services:
app:
image: node:20-alpine
working_dir: /app
volumes:
- .:/app
- ${HOME}/.local/certs:/certs:ro
ports:
- "3443:3443"
environment:
- SSL_CERT=/certs/localhost+4.pem
- SSL_KEY=/certs/localhost+4-key.pem
command: node server.js
Di dalam container, baca dari environment variable:
const options = {
key: fs.readFileSync(process.env.SSL_KEY),
cert: fs.readFileSync(process.env.SSL_CERT),
};
Gotcha yang Gue Pernah Kena
1. Firefox Tetap Complain Setelah Install CA
Firefox punya trust store sendiri yang terpisah dari sistem. Pastikan nss terinstall sebelum kamu jalankan mkcert -install. Kalau sudah terlanjur, uninstall dulu CA-nya lalu install ulang:
mkcert -uninstall
brew install nss # macOS
mkcert -install
Restart Firefox setelah itu.
2. Wildcard Cert Nggak Cover Root Domain
*.app.local cover api.app.local tapi tidak cover app.local sendiri. Jadi selalu include keduanya saat generate:
mkcert app.local "*.app.local"
3. WSL2 dan Windows Browser Beda Trust Store
Kalau kamu develop di WSL2 tapi buka browser di Windows, mkcert -install di WSL2 tidak otomatis trust di browser Windows. Kamu perlu:
- Copy file CA dari WSL2:
# Di WSL2
cat "$(mkcert -CAROOT)/rootCA.pem"
- Import manual ke Windows Certificate Store:
- Buka
certmgr.msc - Import ke "Trusted Root Certification Authorities"
- Restart browser Windows
- Buka
Alternatif yang lebih simple: install mkcert di Windows juga, lalu jalankan mkcert -install dari PowerShell.
4. /etc/hosts Tidak Berlaku untuk Semua App
Beberapa app (terutama yang pakai DNS-over-HTTPS) bypass /etc/hosts. Chrome dan Firefox bisa dikonfigurasi untuk disable DoH di development:
- Chrome:
chrome://flags/#dns-over-https→ Disabled - Firefox: Settings → Privacy → DNS over HTTPS → Off
5. Cert Expired Setelah 2 Tahun
Cert dari mkcert expire setelah 2 tahun. Kalau tiba-tiba browser complain lagi, generate ulang:
cd ~/.local/certs
mkcert localhost 127.0.0.1 ::1 app.local "*.app.local"
# Restart service yang pakai cert tersebut
Verifikasi Setup Berjalan Benar
Setelah setup, verifikasi dengan curl:
# Harusnya return 200 tanpa error
curl -v https://localhost:3443
# Cek detail cert
openssl s_client -connect localhost:3443 -servername localhost < /dev/null 2>/dev/null | \
openssl x509 -noout -text | grep -A2 "Subject Alternative Name"
Output yang benar:
X509v3 Subject Alternative Name:
DNS:localhost, IP Address:127.0.0.1, IP Address:0:0:0:0:0:0:0:1
Kalau kamu pakai Node.js dan mau test programatically:
// test-https.js
const https = require('https');
https.get('https://localhost:3443', (res) => {
console.log('Status:', res.statusCode);
console.log('TLS protocol:', res.socket.getProtocol());
}).on('error', (err) => {
console.error('Error:', err.message);
});
Langkah Lanjutan
Setup local HTTPS development setup ini yang gue pakai sehari-hari untuk semua project lokal. Setelah ini berjalan, beberapa hal yang worth di-explore:
-
Caddy sebagai alternatif Nginx — Caddy punya
tls internaldirective yang otomatis handle local cert tanpa perlumkcertmanual. Cocok kalau kamu mau setup yang lebih simple. -
Automasi dengan script — Buat shell script yang generate cert dan update
/etc/hostssekaligus untuk project baru:
#!/bin/bash
# new-local-project.sh
DOMAIN="$1.local"
echo "127.0.0.1 $DOMAIN" | sudo tee -a /etc/hosts
cd ~/.local/certs && mkcert "$DOMAIN" "*.$DOMAIN"
echo "Done! Akses project di https://$DOMAIN"
-
Sinkronisasi di tim — Kalau develop bareng tim, distribute
rootCA.pemdari satu mesin dan import ke semua anggota tim. Jangan distribute private key CA-nya. -
Integrate ke dev container — Kalau pakai VS Code Dev Containers atau GitHub Codespaces, mount cert dan CA ke container definition supaya HTTPS langsung available tanpa setup ulang. Untuk optimasi database di production, kamu juga bisa merujuk ke WordPress database optimization benchmarks yang menunjukkan dampak performa dari berbagai teknik.
Setup ini makan waktu 15–20 menit pertama kali, tapi setelah itu kamu nggak bakal lagi debug masalah cookie atau mixed content yang sebenarnya cuma masalah HTTP vs HTTPS.