Kodeloka
  1. Kodeloka
  2. Kelas Nginx
  3. Materi

3 · TLS & Keamanan · Modul 5 dari 8

TLS, Sertifikat & HTTPS

Menyiapkan HTTPS yang benar: rantai sertifikat, versi protokol, dan pengalihan yang tidak menjebak.

Konfigurasi Dasar

server {
    listen 443 ssl;
    http2  on;                       # Nginx 1.25+, sebelumnya: listen 443 ssl http2
    server_name contoh.id;

    ssl_certificate     /etc/letsencrypt/live/contoh.id/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/contoh.id/privkey.pem;

    ssl_protocols       TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers off;
    ssl_ciphers         ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;

    ssl_session_cache   shared:SSL:10m;
    ssl_session_timeout 1d;
    ssl_session_tickets off;
}
🚫 fullchain.pem, bukan cert.pem

Kesalahan yang sangat sering terjadi dan sulit dikenali. Dengan cert.pem, peramban desktop tetap bekerja karena sudah menyimpan sertifikat perantara dari kunjungan sebelumnya — sementara klien API, aplikasi seluler, dan curl gagal dengan unable to verify the first certificate. Situs tampak "baik-baik saja" di layar, tetapi rusak bagi separuh klien.

openssl s_client -connect contoh.id:443 -servername contoh.id < /dev/null
# Periksa bagian "Certificate chain" — harus memuat lebih dari satu sertifikat

Pengalihan HTTP ke HTTPS

server {
    listen 80;
    server_name contoh.id www.contoh.id;

    location /.well-known/acme-challenge/ {
        root /var/www/certbot;       # biarkan validasi ACME lewat
    }

    location / {
        return 301 https://$host$request_uri;
    }
}
⚠️ Jangan mengalihkan seluruh port 80 tanpa pengecualian

Validasi HTTP-01 dari Let's Encrypt harus dapat menjangkau /.well-known/acme-challenge/ melalui HTTP biasa. Bila seluruh port 80 dialihkan tanpa pengecualian, perpanjangan sertifikat gagal secara diam-diam — dan baru ketahuan sembilan puluh hari kemudian ketika sertifikat kedaluwarsa.

HSTS

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
🚫 HSTS tidak bisa ditarik kembali

Setelah peramban menerima header ini, ia menolak terhubung melalui HTTP ke domain tersebut selama max-age — bahkan bila Nginx sudah diperbaiki. Dengan includeSubDomains, aturan itu berlaku pula untuk semua subdomain, termasuk yang belum punya sertifikat. Mulailah dengan max-age=300, pastikan seluruh subdomain siap, baru naikkan ke setahun.

server_name Bawaan dan SNI

server {
    listen 443 ssl default_server;
    server_name _;

    ssl_certificate     /etc/nginx/ssl/bawaan.pem;
    ssl_certificate_key /etc/nginx/ssl/bawaan.key;

    return 444;                      # tutup koneksi tanpa balasan
}

Tanpa default_server, blok server pertama yang mendengarkan di port itu menjadi bawaan. Permintaan ke IP langsung atau ke domain asing yang diarahkan ke server ini akan mendapat sertifikat dan konten situs pertama — sering kali bukan yang diinginkan. Kode 444 khusus Nginx menutup koneksi tanpa mengirim apa pun.

OCSP Stapling

ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/letsencrypt/live/contoh.id/chain.pem;
resolver 1.1.1.1 8.8.8.8 valid=300s;

Tanpa stapling, peramban menghubungi penerbit sertifikat sendiri untuk memeriksa status pencabutan — menambah satu perjalanan jaringan pada setiap kunjungan pertama. Dengan stapling, Nginx mengambil bukti itu berkala dan menyertakannya langsung dalam jabat tangan.

💡 Stapling butuh resolver

Nginx harus dapat menyelesaikan nama host penanggap OCSP. Bila direktif resolver tidak ada, stapling gagal diam-diam dan hanya tercatat sebagai peringatan di log kesalahan.