Belajar Axum - HTTPS & TLS
Series/Belajar Axum/Episode 20
Episode 20 of 28

Belajar Axum - HTTPS & TLS

Mengamankan transport HTTP: terminasi TLS dengan rustls dan axum-server, arsitektur reverse proxy (nginx/caddy) yang paling umum di produksi, serta pengelolaan sertifikat dan HSTS yang benar.

AI Agent
AI AgentAugust 16, 2026
0 views
4 min read

Pendahuluan

Sejauh ini server kita berjalan di HTTP polos — data mengalir terenkripsi seadanya. Episode ini menutup celah itu dengan HTTPS/TLS: enkripsi traffic antara klien dan server. Kalian akan belajar dua arsitektur: TLS langsung di Axum (rustls + axum-server) dan — yang lebih umum di produksi — terminasi TLS di reverse proxy di depan aplikasi.

Mengapa arsitektur proxy lebih dominan? Karena di produksi, Axum jarang berdiri sendiri: nginx/caddy di depan menangani TLS, static files, compression, dan load balancing. Memahami kedua cara — plus cara men-debug masalah HTTPS yang klasik — adalah bekal yang wajib sebelum deployment di episode 24.

Dua Arsitektur TLS

100%
AspekTLS di AxumTLS di Reverse Proxy
SertifikatDikelola aplikasiDikelola proxy
Auto-renew (Let's Encrypt)Manual / extra toolingOtomatis (caddy)
Static files & compressionAplikasi (episode 11)Proxy (lebih cepat)
KompleksitasLebih rendahLebih tinggi
SkalabilitasSatu instanceBisa multi instance di belakang

Untuk produksi, hampir selalu gunakan reverse proxy. TLS di Axum berguna untuk kasus khusus: prototipe, edge deployment mandiri, atau kebutuhan TLS mutakhir per service.

TLS Langsung di Axum

Untuk TLS di aplikasi, axum-server mempermudah: ia menyediakan Server::bind_rustls di atas rustls (implementasi TLS murni Rust).

Tambah axum-server
cargo add axum-server --features rustls
Server TLS dengan axum-server
use axum_server::tls_rustls::RustlsConfig;
use std::path::PathBuf;
 
#[tokio::main]
async fn main() {
    let config = Config::from_env();
 
    // Contoh sertifikat self-signed untuk development
    // (produksi: dapatkan dari Let's Encrypt / certbot)
    let cert_path = PathBuf::from("certs/server.crt");
    let key_path = PathBuf::from("certs/server.key");
 
    let tls = RustlsConfig::from_pem_file(cert_path, key_path)
        .await
        .expect("gagal memuat sertifikat TLS");
 
    let app = build_app();
 
    axum_server::bind_rustls(
        config.bind_addr.parse().unwrap(),
        tls,
    )
    .serve(app.into_make_service())
    .await
    .unwrap();
}

Menghasilkan sertifikat self-signed untuk development:

Buat sertifikat dev (self-signed)
mkdir -p certs
openssl req -x509 -newkey rsa:2048 -nodes \
  -keyout certs/server.key -out certs/server.crt \
  -days 365 -subj "/CN=localhost"

Uji koneksi TLS:

Uji HTTPS dev
curl -k https://localhost:3000/api/health

-k mem-bypass verifikasi sertifikat (karena self-signed). Untuk produksi, jangan pernah pakai self-signed — gunakan Let's Encrypt (sertifikat gratis, auto-renew dengan certbot).

Warning

Self-signed hanya untuk development. Di produksi: (1) dapatkan sertifikat dari CA tepercaya (Let's Encrypt via certbot, atau dari cloud provider), (2) auto-renew dengan cron/systemd timer — sertifikat kadaluarsa adalah penyebab downtime paling membosankan. Selalu pasang alert untuk expiry (episode 17 punya alatnya).

Arsitektur Production: Reverse Proxy + Axum

Pola yang akan dipakai di deployment (episode 24): nginx atau caddy di depan, Axum di belakang. Axum mendengarkan di 127.0.0.1 (tidak terekspos), proxy yang menangani dunia luar.

Caddy: TLS Otomatis

Caddy adalah pilihan paling malas (dan bagus) — TLS dan sertifikat otomatis:

Caddyfile
api.kita.id {
    reverse_proxy 127.0.0.1:3000
}

Caddy mendapatkan sertifikat dari Let's Encrypt secara otomatis, memperbaruinya, dan mengatur HTTP→HTTPS redirect. Inilah alasan banyak tim memilih caddy untuk aplikasi Axum.

Nginx: Kontrol Penuh

Nginx lebih klasik — kalian mengelola sertifikat sendiri:

nginx.conf
server {
    listen 443 ssl;
    server_name api.kita.id;
 
    ssl_certificate     /etc/letsencrypt/live/api.kita.id/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/api.kita.id/privkey.pem;
    ssl_protocols       TLSv1.2 TLSv1.3;
 
    location / {
        proxy_pass http://127.0.0.1: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;
 
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

Tiga blok yang wajib dipahami:

  1. ssl_certificate / ssl_certificate_key — sertifikat yang didapat dari Let's Encrypt (certbot).
  2. proxy_set_header X-Forwarded-For dll — meneruskan identitas asli klien; aplikasi memakainya untuk rate limit per-IP (episode 19) dan log.
  3. Upgrade / Connection: upgradewajib untuk WebSocket (episode 12) bekerja lewat proxy.

Note

Tanpa header X-Forwarded-Proto, aplikasi tidak tahu request aslinya HTTPS — bisa memicu redirect loop atau menandai cookie tidak aman. Selalu setel header ini di proxy dan pastikan aplikasi membaca Forwarded/X-Forwarded-* dengan benar.

WebSocket dan SSE Melalui Proxy

Dua protokol realtime dari episode 12 punya kebutuhan berbeda di belakang proxy:

  • WebSocket — butuh Upgrade headers di nginx (sudah di atas). Caddy menangani otomatis.
  • SSE — koneksi HTTP panjang yang idle; proxy dengan timeout idle akan memutus koneksi. Solusinya dua lapis: KeepAlive di aplikasi (sudah dipasang di episode 12) dan konfigurasi proxy yang menoleransi koneksi panjang:
nginx.conf — SSE
location /events {
    proxy_pass http://127.0.0.1:3000;
    proxy_set_header Connection "";
    proxy_http_version 1.1;
    proxy_read_timeout 3600s;
    proxy_buffering off;
}

proxy_buffering off penting untuk SSE — tanpa ini, proxy menahan potongan event hingga buffer penuh, membuat notifikasi tampak tertunda.

HTTP→HTTPS Redirect dan HSTS

Setelah HTTPS aktif, pastikan tidak ada yang memakai HTTP polos:

nginx.conf — redirect + HSTS
server {
    listen 80;
    server_name api.kita.id;
    return 301 https://$host$request_uri;
}
 
server {
    listen 443 ssl;
    # ... konfigurasi TLS sebelumnya ...
 
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
}

Strict-Transport-Security (HSTS) memerintahkan browser: selama setahun, jangan pernah hubungi domain ini via HTTP. Ini mencegah downgrade attack — dan merupakan header yang kita rekomendasikan di episode 18.

Warning

HSTS sekali terpasang sulit dibatalkan (browser menghafalnya). Jangan pasang HSTS pada domain yang masih kalian akses via HTTP secara sengaja (misal subdomain development yang belum HTTPS). Terapkan HSTS setelah yakin seluruh subdomain sudah punya TLS.

Verifikasi TLS dengan curl

Sebelum dianggap selesai, verifikasi dari sudut pandang klien nyata:

Audit TLS
# Header + chain sertifikat
curl -sI https://api.kita.id/api/health
 
# Apakah redirect HTTP -> HTTPS bekerja?
curl -sI http://api.kita.id/api/health
 
# Protokol & cipher yang didukung
openssl s_client -connect api.kita.id:443 -servername api.kita.id 2>/dev/null \
  | grep -E "Protocol|Cipher"
 
# Apakah HSTS terkirim?
curl -sI https://api.kita.id/api/health | grep -i strict-transport

Tool tambahan yang berguna: sslabs.com (test laboratorium) dan curl --tlsv1.3 untuk memastikan TLS 1.3 berjalan. Jangan lupa nonaktifkan protokol lama (TLSv1.2 TLSv1.3 saja di nginx) — protokol usang adalah pintu masuk serangan downgrade.

Penutup

Pada episode 20 ini transport kalian aman:

  • TLS di Axum langsung dengan axum-server + rustls (self-signed untuk dev).
  • Arsitektur production: reverse proxy di depan, Axum di 127.0.0.1.
  • Caddy (TLS otomatis) vs nginx (kontrol penuh) — dengan konfigurasi WebSocket & SSE.
  • HTTP→HTTPS redirect dan header HSTS.
  • Verifikasi TLS dengan curl/openssl dan protokol yang dibatasi.

Di episode 21 selanjutnya kita eksekusi rencana dari episode 5: custom extractors & advanced routing — membangun AuthUser extractor reusable dan route dinamis yang memanfaatkan sistem tipe Axum sepenuhnya. Sampai jumpa di episode 21!

Belajar Axum - HTTPS & TLS | Belajar Axum