HAProxy Cheatsheet: Port Forwarding & Domain Forwarding

Networking #cli#forwarding#haproxy#linux Last updated
Library

Panduan praktis untuk Ubuntu/Debian. Ditujukan bagi yang sudah paham perintah dasar Linux tetapi baru mengenal HAProxy.

Status validasi. Semua blok konfigurasi lengkap di dokumen ini sudah dicek sintaksnya dengan haproxy -c pada HAProxy 2.8.16 (Ubuntu 24.04). Contoh A, B, C, dan routing SNI di Contoh D juga diuji dengan lalu lintas nyata (backend dummy di loopback, sertifikat self-signed). Contoh E (SSH/database, allowlist IP) dan proses penerbitan/perpanjangan Let's Encrypt hanya divalidasi sintaksnya, belum diuji end-to-end. Selalu validasi ulang di versi HAProxy Anda sendiri (lihat Bagian 10).

Semua contoh bersifat ilustrasi, bukan konfigurasi produksi siap pakai. IP, domain, dan path adalah placeholder. Ganti sesuai lingkungan Anda.

Daftar Isi#

  1. Konsep dasar
  2. Instalasi
  3. Dasar file konfigurasi
  4. Cara memakai contoh di dokumen ini
  5. Contoh A: port forwarding sederhana
  6. Contoh B: forwarding berdasarkan domain (HTTP)
  7. Contoh C: HTTPS dengan TLS termination
  8. Contoh D: HTTPS TCP passthrough
  9. Contoh E: beberapa port sekaligus
  10. Alur aman mengubah konfigurasi
  11. Checklist jaringan & firewall
  12. Error umum & troubleshooting
  13. Quick reference
  14. Praktik keamanan
  15. Referensi resmi

1. Konsep dasar#

Dokumentasi resmi: Frontends · Backends

Istilah Artinya (sederhana)
Reverse proxy Server di depan aplikasi Anda. Klien hanya bicara dengan proxy, proxy meneruskan ke server asli di belakangnya.
frontend "Pintu depan": IP dan port tempat HAProxy menerima koneksi dari klien, plus aturan ke mana koneksi itu dikirim.
backend "Tujuan": kumpulan server asli yang menerima traffic yang diteruskan HAProxy.
bind Baris di frontend yang menentukan IP:port yang didengarkan. Contoh: bind :80 atau bind 203.0.113.10:443.
server Baris di backend yang mendefinisikan satu server tujuan: server nama IP:port [opsi].
mode http HAProxy memahami HTTP (Host header, path, header lain). Dipakai untuk routing berdasarkan domain/path.
mode tcp HAProxy hanya meneruskan byte mentah tanpa memahami isinya. Cocok untuk SSH, database, atau TLS passthrough.
default_backend Backend "bawaan": dipakai jika tidak ada aturan use_backend yang cocok.
use_backend Pilih backend jika suatu kondisi (ACL) terpenuhi. Contoh: use_backend bk_app if host_app.
ACL Access Control List: kondisi yang diberi nama, misalnya "Host header sama dengan app.example.com".
TLS termination HAProxy yang membuka (dekripsi) HTTPS. Sertifikat dipasang di HAProxy; ke backend biasanya diteruskan HTTP biasa.
TCP passthrough HAProxy meneruskan koneksi terenkripsi tanpa membukanya. Sertifikat tetap di server backend.

Alur traffic#

Client (browser / aplikasi)
  |
  v
Public IP :80 / :443            <- frontend (bind)
  |
  v
HAProxy                         <- ACL / use_backend menentukan tujuan
  |
  +--> app.example.com --> backend bk_app --> 192.168.1.20:8080
  |
  +--> api.example.com --> backend bk_api --> 192.168.1.30:3000

Peran DNS vs HAProxy#

  • DNS hanya mengubah nama domain menjadi alamat IP. DNS tidak meneruskan traffic.
  • HAProxy yang menerima koneksi di IP tersebut lalu meneruskannya ke backend.
  • Karena itu, record DNS (A/AAAA) semua domain yang ingin Anda layani harus menunjuk ke IP publik HAProxy, bukan ke IP backend.

2. Instalasi#

Perintah untuk Ubuntu/Debian:

sudo apt update
sudo apt install haproxy
Tujuan Perintah
Cek versi haproxy -v
Cek versi + fitur build (OpenSSL dll.) haproxy -vv
Cek status service sudo systemctl status haproxy
Aktifkan saat boot sudo systemctl enable haproxy
Matikan saat boot sudo systemctl disable haproxy
Start sudo systemctl start haproxy
Stop sudo systemctl stop haproxy
Reload (tanpa memutus koneksi aktif) sudo systemctl reload haproxy
Restart (memutus semua koneksi) sudo systemctl restart haproxy
Lokasi file konfigurasi /etc/haproxy/haproxy.cfg

Paket Debian/Ubuntu biasanya sudah meng-enable dan menjalankan service setelah instalasi. Pastikan dengan systemctl status. Versi HAProxy yang terpasang bergantung pada rilis OS Anda, jadi selalu cek haproxy -v.


3. Dasar file konfigurasi#

File utama: /etc/haproxy/haproxy.cfg. Strukturnya:

global
    # Pengaturan level proses (user, log, chroot, TLS default)

defaults
    # Nilai bawaan untuk semua frontend/backend di bawahnya (timeout, log, mode)

frontend nama_frontend
    # Pendengar sisi klien (bind, ACL, pemilihan backend)

backend nama_backend
    # Server tujuan
Seksi Fungsi
global Berlaku untuk seluruh proses HAProxy: log, user yang menjalankan proses, default TLS.
defaults Nilai bawaan untuk seksi di bawahnya (timeout, log). Bisa ditimpa di tiap frontend/backend.
frontend Menentukan IP:port yang didengarkan dan ke backend mana traffic dikirim.
backend Menentukan server tujuan beserta health check.

Konfigurasi minimal yang berfungsi#

global
    log /dev/log local0
    log /dev/log local1 notice
    chroot /var/lib/haproxy
    stats socket /run/haproxy/admin.sock mode 660 level admin
    stats timeout 30s
    user haproxy
    group haproxy
    daemon

defaults
    log     global
    timeout connect 5s
    timeout client  30s
    timeout server  30s

frontend ft_web
    mode http
    bind :80
    option httplog
    default_backend bk_web

backend bk_web
    mode http
    server web1 192.168.1.20:8080 check

Penjelasan baris penting:

Baris Arti
log /dev/log local0 Kirim log ke syslog lokal. Di Ubuntu/Debian biasanya masuk ke /var/log/haproxy.log lewat rsyslog.
chroot /var/lib/haproxy Proses dikurung di direktori ini setelah start (keamanan).
stats socket ... level admin Socket untuk runtime API. Hak aksesnya dibatasi mode 660.
user haproxy / group haproxy Setelah membuka port dan membaca sertifikat, proses turun hak ke user ini.
daemon Jalan di background.
log global Pakai pengaturan log dari global.
timeout connect 5s Batas waktu HAProxy membuka koneksi ke backend.
timeout client 30s Batas idle di sisi klien.
timeout server 30s Batas idle/menunggu di sisi backend.
frontend ft_web Nama frontend (bebas, tetapi harus unik).
mode http Perlakukan traffic sebagai HTTP.
bind :80 Dengarkan port 80 di semua IP server.
option httplog Format log HTTP yang detail (hanya untuk mode http).
default_backend bk_web Semua request ke backend bk_web.
backend bk_web Nama backend. Harus sama persis dengan yang dirujuk frontend.
server web1 192.168.1.20:8080 check Server tujuan. check = HAProxy rutin mengecek apakah server hidup.

Penting. File bawaan paket Debian/Ubuntu sudah memiliki global dan defaults (termasuk errorfile dan setelan TLS). Contoh di dokumen ini menyederhanakannya. Jangan menimpa file produksi Anda secara buta (lihat bagian berikut).


4. Cara memakai contoh di dokumen ini (penting)#

  1. Setiap contoh (A–E) berdiri sendiri dan berisi global/defaults/frontend/backend lengkap supaya bisa divalidasi sendiri-sendiri.
  2. Contoh tidak boleh digabung mentah-mentah. Banyak yang memakai port yang sama:
Contoh Port yang di-bind Bentrok dengan
A 9000 E (E juga memakai 9000; isinya aturan yang sama)
B 80 C (C juga memakai 80)
C 80, 443 B (port 80), D (port 443)
D 443 C
E 2222, 9000, 5433 A (port 9000)

Dua frontend tidak boleh bind ke IP:port yang sama. 3. Cara aman mencoba: simpan contoh sebagai file terpisah dan validasi dulu tanpa menyentuh konfigurasi aktif.

nano /tmp/contoh-a.cfg                 # tempel contoh
haproxy -c -f /tmp/contoh-a.cfg        # validasi saja, tidak menjalankan apa pun
  1. Cara menerapkan ke server yang sudah berjalan: gabungkan hanya seksi frontend/backend yang diperlukan ke /etc/haproxy/haproxy.cfg (setelah backup), pertahankan global/defaults milik Anda, lalu ikuti Bagian 10. Sesuaikan juga timeout di defaults Anda jika perlu.
  2. Setiap frontend dan backend harus memakai mode yang sama (keduanya http atau keduanya tcp). Mismatch membuat konfigurasi gagal atau tidak bekerja.

5. Contoh A: port forwarding sederhana#

Dokumentasi: TCP proxying

Skenario (IP adalah placeholder; 203.0.113.0/24 adalah rentang contoh dokumentasi):

  • HAProxy: IP publik 203.0.113.10
  • Backend: 192.168.1.20, aplikasi di port 8080
  • Tujuan: koneksi ke HAProxy port 9000 diteruskan ke 192.168.1.20:8080

File: /tmp/contoh-a.cfg (untuk uji) atau digabung ke /etc/haproxy/haproxy.cfg.

global
    log /dev/log local0
    log /dev/log local1 notice
    chroot /var/lib/haproxy
    stats socket /run/haproxy/admin.sock mode 660 level admin
    stats timeout 30s
    user haproxy
    group haproxy
    daemon

defaults
    log     global
    timeout connect 5s
    timeout client  30s
    timeout server  30s

frontend ft_port_9000
    mode tcp
    bind :9000
    option tcplog
    default_backend bk_app_tcp

backend bk_app_tcp
    mode tcp
    server app1 192.168.1.20:8080 check

Penjelasan:

  • Port yang didengarkan HAProxy: 9000 (bind :9000).
  • Port yang menerima di backend: 8080 (bagian akhir server app1 192.168.1.20:8080).
  • Kenapa mode tcp: untuk meneruskan koneksi generik (bisa HTTP, SSH, database, protokol apa pun) HAProxy cukup menyalurkan byte tanpa perlu paham isinya.
  • mode tcp tidak bisa membaca header HTTP. Di mode ini HAProxy tidak melihat Host, path, atau cookie. Jangan memakai ACL berbasis HTTP (hdr(host), path_beg, dll.) di frontend mode tcp. Untuk itu gunakan mode http (Contoh B).
  • Dua sisi harus terjangkau: (1) klien harus bisa mencapai HAProxy:9000 (firewall/NAT terbuka), dan (2) HAProxy harus bisa mencapai 192.168.1.20:8080. Jika salah satu putus, koneksi gagal.
  • check membuat HAProxy mengecek TCP connect ke backend; jika gagal, server ditandai DOWN dan klien mendapat koneksi ditutup.

Cara menguji:

# 1) Dari server HAProxy: backend terjangkau?
nc -vz 192.168.1.20 8080
# Hasil yang diharapkan: "... succeeded!" / "open"

# 2) Dari klien: lewat HAProxy
curl -v http://203.0.113.10:9000/
# Hasil yang diharapkan: respons aplikasi Anda (jika aplikasinya HTTP)

# 3) Untuk non-HTTP, cukup tes port
nc -vz 203.0.113.10 9000

6. Contoh B: forwarding berdasarkan domain (HTTP)#

Dokumentasi: HTTP proxying

Skenario (placeholder):

  • app.example.com → 192.168.1.20:8080
  • api.example.com → 192.168.1.30:3000
  • HAProxy mendengarkan HTTP di port 80
  • Domain yang tidak dikenal ditolak dengan 404

File: /tmp/contoh-b.cfg (uji) atau digabung ke /etc/haproxy/haproxy.cfg.

global
    log /dev/log local0
    log /dev/log local1 notice
    chroot /var/lib/haproxy
    stats socket /run/haproxy/admin.sock mode 660 level admin
    stats timeout 30s
    user haproxy
    group haproxy
    daemon

defaults
    log     global
    timeout connect 5s
    timeout client  30s
    timeout server  30s

frontend ft_http
    mode http
    bind :80
    option httplog
    timeout http-request 10s

    acl host_app hdr(host),field(1,:) -i app.example.com
    acl host_api hdr(host),field(1,:) -i api.example.com

    use_backend bk_app if host_app
    use_backend bk_api if host_api
    default_backend bk_reject

backend bk_app
    mode http
    option forwardfor
    server app1 192.168.1.20:8080 check

backend bk_api
    mode http
    option forwardfor
    server api1 192.168.1.30:3000 check

backend bk_reject
    mode http
    http-request deny deny_status 404

Penjelasan per baris:

Baris Arti
timeout http-request 10s Klien harus selesai mengirim request dalam 10 detik (melindungi dari koneksi lambat).
acl host_app hdr(host),field(1,:) -i app.example.com Buat kondisi bernama host_app: true jika header Host sama dengan app.example.com. -i = tidak peka huruf besar/kecil. field(1,:) mengambil bagian sebelum : supaya Host: app.example.com:80 tetap cocok.
use_backend bk_app if host_app Jika ACL host_app true, pakai backend bk_app.
default_backend bk_reject Domain lain (atau akses lewat IP) masuk ke bk_reject.
option forwardfor Menambahkan header X-Forwarded-For berisi IP klien asli supaya aplikasi bisa mencatatnya.
server app1 ... check Server tujuan + health check.
http-request deny deny_status 404 Di bk_reject: balas langsung 404 tanpa meneruskan ke server mana pun.

Menambah domain baru tanpa mengganti seluruh konfigurasi#

Misalnya menambah blog.example.com → 192.168.1.40:8081. Ada 3 perubahan kecil:

# 1) Di frontend ft_http, tambahkan ACL dan use_backend
    acl host_blog hdr(host),field(1,:) -i blog.example.com
    use_backend bk_blog if host_blog        # letakkan sebelum default_backend

# 2) Tambahkan backend baru (di bagian bawah file)
backend bk_blog
    mode http
    option forwardfor
    server blog1 192.168.1.40:8081 check
  1. Buat record DNS blog.example.com yang menunjuk ke IP publik HAProxy. Lalu validasi dan reload (Bagian 10).

Prasyarat DNS. Record DNS semua domain (app, api, blog, ...) harus menunjuk ke IP publik HAProxy, bukan ke IP backend. Tanpa itu, request tidak pernah sampai ke HAProxy.

Cara menguji sebelum DNS siap (memaksa Host header):

curl -v -H "Host: app.example.com" http://203.0.113.10/
curl -v --resolve api.example.com:80:203.0.113.10 http://api.example.com/
curl -i -H "Host: tidak-dikenal.example.com" http://203.0.113.10/
# Hasil yang diharapkan untuk host tidak dikenal: HTTP/1.1 404

7. Contoh C: HTTPS dengan TLS termination#

Dokumentasi: HTTP proxying · Configuration Manual (ssl, crt)

Di sini HAProxy membuka TLS di port 443, lalu meneruskan HTTP biasa ke backend.

7.1 Dari mana sertifikat berasal?#

Menginstal HAProxy tidak memberi Anda sertifikat HTTPS. Anda harus menyediakannya sendiri, misalnya:

  • Let's Encrypt (gratis, via certbot atau acme.sh), atau
  • sertifikat dari CA komersial, atau
  • sertifikat internal / self-signed (hanya untuk uji, browser akan memberi peringatan).

7.2 Format file PEM untuk HAProxy#

HAProxy membaca satu file PEM yang berisi sertifikat + chain + private key (digabung). Urutan umum: sertifikat server, sertifikat intermediate (chain), lalu private key.

sudo mkdir -p /etc/haproxy/certs
# Contoh dengan hasil Let's Encrypt (fullchain.pem sudah berisi cert + intermediate):
sudo sh -c 'cat /etc/letsencrypt/live/app.example.com/fullchain.pem \
                /etc/letsencrypt/live/app.example.com/privkey.pem \
                > /etc/haproxy/certs/app.example.com.pem'

Izin file yang aman (private key tidak boleh bisa dibaca user lain):

sudo chown -R root:root /etc/haproxy/certs
sudo chmod 750 /etc/haproxy/certs
sudo chmod 600 /etc/haproxy/certs/*.pem

HAProxy dijalankan systemd sebagai root lalu menurunkan hak setelah membaca sertifikat, sehingga root:root 600 cukup. Jika Anda menjalankan HAProxy dengan cara lain/non-root, sesuaikan pemilik filenya.

7.3 Cek dukungan TLS di versi HAProxy Anda#

haproxy -vv | grep -i -E "openssl|ssl"
# Harus muncul baris seperti: "Built with OpenSSL version : ..." dan "Running on OpenSSL version : ..."

Jika tidak ada dukungan OpenSSL/SSL, direktif ssl crt ... akan gagal pada validasi. Direktif ssl-min-ver (di contoh) membutuhkan HAProxy yang cukup baru (1.8 ke atas); haproxy -c akan memberi tahu jika ada yang tidak didukung.

7.4 Konfigurasi#

File: /tmp/contoh-c.cfg (uji) atau digabung ke /etc/haproxy/haproxy.cfg. Letakkan .pem di /etc/haproxy/certs/ lebih dulu, karena haproxy -c gagal jika file/direktori sertifikat tidak ada atau kosong.

global
    log /dev/log local0
    log /dev/log local1 notice
    chroot /var/lib/haproxy
    stats socket /run/haproxy/admin.sock mode 660 level admin
    stats timeout 30s
    user haproxy
    group haproxy
    daemon
    ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets

defaults
    log     global
    timeout connect 5s
    timeout client  30s
    timeout server  30s

# --- Port 80: redirect ke HTTPS (kecuali challenge ACME) ---
frontend ft_http
    mode http
    bind :80
    option httplog
    timeout http-request 10s

    acl acme_challenge path_beg /.well-known/acme-challenge/
    http-request redirect scheme https code 301 unless acme_challenge
    use_backend bk_certbot if acme_challenge

# --- Port 443: TLS termination + routing per domain ---
frontend ft_https
    mode http
    bind :443 ssl crt /etc/haproxy/certs/
    option httplog
    option forwardfor
    timeout http-request 10s
    timeout http-keep-alive 10s

    http-request set-header X-Forwarded-Proto https
    # Opsional, aktifkan hanya jika HTTPS sudah stabil di semua domain:
    # http-response set-header Strict-Transport-Security "max-age=31536000"

    acl host_app hdr(host),field(1,:) -i app.example.com
    acl host_api hdr(host),field(1,:) -i api.example.com

    use_backend bk_app if host_app
    use_backend bk_api if host_api
    default_backend bk_reject

backend bk_app
    mode http
    server app1 192.168.1.20:8080 check

backend bk_api
    mode http
    server api1 192.168.1.30:3000 check

backend bk_reject
    mode http
    http-request deny deny_status 404

# Backend khusus untuk validasi certbot (--standalone di port 8888)
backend bk_certbot
    mode http
    server certbot 127.0.0.1:8888

Penjelasan inti:

Baris Arti
bind :443 ssl crt /etc/haproxy/certs/ Dengarkan 443 dengan TLS. Menunjuk direktori = semua file .pem di dalamnya dimuat, dan HAProxy memilih sertifikat yang cocok dengan SNI klien. Anda juga bisa menunjuk satu file: crt /etc/haproxy/certs/app.example.com.pem.
ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets Tolak TLS di bawah 1.2 untuk semua bind ... ssl.
http-request redirect scheme https code 301 unless acme_challenge Semua HTTP dialihkan ke HTTPS, kecuali path validasi ACME (supaya perpanjangan sertifikat tetap bisa lewat port 80).
http-request set-header X-Forwarded-Proto https Memberi tahu aplikasi backend bahwa klien asli memakai HTTPS.
timeout http-keep-alive 10s Batas menunggu request berikutnya pada koneksi keep-alive.
server app1 192.168.1.20:8080 check Ke backend dikirim HTTP biasa (tanpa ssl), karena TLS sudah dibuka di HAProxy.

Ingat: aturan http-request dijalankan sebelum use_backend. Itu sebabnya pengecualian ACME ditulis langsung pada baris redirect (unless acme_challenge).

7.5 Penerbitan pertama & perpanjangan sertifikat (Let's Encrypt)#

HAProxy memakai port 80, jadi certbot --standalone di port 80 akan bentrok. Pola yang rapi: HAProxy meneruskan path /.well-known/acme-challenge/ ke 127.0.0.1:8888 (sudah ada di contoh), dan certbot dijalankan di port 8888.

Urutan pertama kali (karena ft_https butuh sertifikat agar bisa dimuat):

  1. Jalankan HAProxy dengan hanya ft_http, bk_certbot, dan backend lain (beri komentar pada ft_https), lalu validasi dan reload.
  2. Terbitkan sertifikat:
    sudo certbot certonly --standalone --http-01-port 8888 \
         -d app.example.com -d api.example.com
    
  3. Buat file PEM dan atur izinnya (lihat 7.2).
  4. Aktifkan kembali ft_https, lalu haproxy -c dan reload.

Perpanjangan otomatis: certbot memperbarui file di /etc/letsencrypt/live/..., tetapi HAProxy tidak membaca file itu langsung. Anda perlu menyusun ulang PEM dan me-reload HAProxy setiap perpanjangan. Buat deploy hook:

sudo tee /etc/letsencrypt/renewal-hooks/deploy/haproxy.sh >/dev/null <<'EOF'
#!/bin/bash
set -e
DOMAIN="$(basename "$RENEWED_LINEAGE")"
cat "$RENEWED_LINEAGE/fullchain.pem" "$RENEWED_LINEAGE/privkey.pem" \
    > "/etc/haproxy/certs/${DOMAIN}.pem"
chmod 600 "/etc/haproxy/certs/${DOMAIN}.pem"
haproxy -c -f /etc/haproxy/haproxy.cfg
systemctl reload haproxy
EOF
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/haproxy.sh
sudo certbot renew --dry-run

certbot renew --dry-run mensimulasikan perpanjangan. Hook baru benar-benar dijalankan pada perpanjangan sungguhan, jadi cek sudo journalctl -u haproxy -n 50 --no-pager setelah perpanjangan pertama.

7.6 Menguji HTTPS#

# Cek sertifikat yang disajikan untuk SNI tertentu
openssl s_client -connect 203.0.113.10:443 -servername app.example.com </dev/null 2>/dev/null \
  | openssl x509 -noout -subject -issuer -dates

# Cek respons dan redirect
curl -vI https://app.example.com
curl -I  http://app.example.com
# Hasil yang diharapkan: "HTTP/1.1 301 Moved Permanently" dengan "location: https://app.example.com/"

8. Contoh D: HTTPS TCP passthrough#

Dokumentasi: TCP proxying

Pada passthrough, HAProxy tidak membuka TLS: ia hanya menyalurkan koneksi terenkripsi ke backend. Sertifikat dan private key tetap berada di server backend.

8.1 Passthrough ke satu backend#

File: /tmp/contoh-d1.cfg (uji) atau digabung ke /etc/haproxy/haproxy.cfg.

global
    log /dev/log local0
    log /dev/log local1 notice
    chroot /var/lib/haproxy
    stats socket /run/haproxy/admin.sock mode 660 level admin
    stats timeout 30s
    user haproxy
    group haproxy
    daemon

defaults
    log     global
    timeout connect 5s
    timeout client  30s
    timeout server  30s

frontend ft_tls_pass
    mode tcp
    bind :443
    option tcplog
    default_backend bk_tls_app

backend bk_tls_app
    mode tcp
    server app1 192.168.1.20:443 check

Seluruh koneksi ke port 443 HAProxy diteruskan ke 192.168.1.20:443. Backend itulah yang melakukan TLS handshake.

8.2 Passthrough banyak domain lewat satu port (SNI)#

Di mode TCP, HAProxy tidak bisa membaca Host header (isinya terenkripsi). Namun nama domain dikirim tidak terenkripsi di awal handshake TLS lewat ekstensi SNI (Server Name Indication). HAProxy bisa membaca SNI itu untuk memilih backend. Ia perlu menunggu ClientHello terlebih dulu, sehingga dibutuhkan tcp-request inspect-delay dan tcp-request content accept.

global
    log /dev/log local0
    log /dev/log local1 notice
    chroot /var/lib/haproxy
    stats socket /run/haproxy/admin.sock mode 660 level admin
    stats timeout 30s
    user haproxy
    group haproxy
    daemon

defaults
    log     global
    timeout connect 5s
    timeout client  30s
    timeout server  30s

frontend ft_tls_sni
    mode tcp
    bind :443
    option tcplog

    # Tunggu ClientHello TLS (maksimal 5 detik) agar SNI bisa dibaca
    tcp-request inspect-delay 5s
    tcp-request content accept if { req.ssl_hello_type 1 }
    # Bukan TLS / terlalu lambat: tutup koneksi
    tcp-request content reject if WAIT_END

    use_backend bk_tls_app if { req.ssl_sni -i app.example.com }
    use_backend bk_tls_api if { req.ssl_sni -i api.example.com }
    # Tanpa default_backend: SNI yang tidak dikenal tidak diteruskan ke mana pun

backend bk_tls_app
    mode tcp
    server app1 192.168.1.20:443 check

backend bk_tls_api
    mode tcp
    server api1 192.168.1.30:443 check

Catatan:

  • req.ssl_sni adalah sample fetch khusus TLS yang membaca SNI dari ClientHello. Jangan memakai hdr(host) atau path_beg di frontend mode tcp: itu ACL HTTP dan butuh mode http (di passthrough isinya terenkripsi sehingga tidak bisa dibaca). Hati-hati: pada pengujian di HAProxy 2.8.16, ACL seperti itu di frontend mode tcp lolos haproxy -c tanpa peringatan, tetapi tidak akan pernah cocok. Hasilnya routing diam-diam tidak berjalan, jadi validasi sintaks saja tidak cukup; selalu uji dengan traffic nyata.
  • Klien yang tidak mengirim SNI (misalnya akses langsung via IP, atau klien lama) tidak akan cocok dengan aturan mana pun. Tambahkan default_backend jika Anda ingin menanganinya.
  • Karena TCP murni, backend melihat IP HAProxy sebagai sumber koneksi, bukan IP klien. Backend yang mendukung PROXY protocol bisa menerima IP klien asli (send-proxy di baris server), tetapi backend harus diaktifkan untuk itu dulu; jika tidak, koneksi akan gagal.

8.3 Perbandingan: TLS termination vs TCP passthrough#

Aspek TLS termination (Contoh C) TCP passthrough (Contoh D)
Di mana TLS dibuka (dekripsi)? Di HAProxy Di server backend
Di mana sertifikat dipasang? Di HAProxy (/etc/haproxy/certs/) Di setiap server backend
Bisa melihat path/header HTTP? Ya (mode http) Tidak (hanya SNI yang terlihat)
Routing berdasarkan Host header, path, header lain SNI saja
Traffic HAProxy → backend HTTP biasa (atau HTTPS jika diatur) Tetap terenkripsi end-to-end
Alasan memilih Sertifikat terpusat, redirect, header, ACL path, logging HTTP Enkripsi end-to-end, backend mengelola sertifikat sendiri, protokol bukan HTTP

9. Contoh E: beberapa port sekaligus#

Dokumentasi: TCP proxying

Skenario (IP backend adalah placeholder):

Port publik Tujuan
2222 SSH 192.168.1.10:22
9000 Aplikasi 192.168.1.20:8080
5433 Database PostgreSQL 192.168.1.40:5432

Strukturnya: satu frontend + satu backend per layanan. Ini paling jelas dan mudah dibatasi per layanan.

Peringatan keamanan. Membuka SSH dan terutama database ke internet sangat berisiko (brute force, eksploit, kebocoran data). Batasi akses dengan firewall, allowlist IP sumber, atau VPN. Di contoh ini 198.51.100.0/24 (rentang contoh dokumentasi) adalah placeholder IP tepercaya Anda. Jangan membuka port database ke publik tanpa pembatasan akses.

File: /tmp/contoh-e.cfg (uji) atau digabung ke /etc/haproxy/haproxy.cfg.

global
    log /dev/log local0
    log /dev/log local1 notice
    chroot /var/lib/haproxy
    stats socket /run/haproxy/admin.sock mode 660 level admin
    stats timeout 30s
    user haproxy
    group haproxy
    daemon

defaults
    log     global
    timeout connect 5s
    timeout client  30s
    timeout server  30s

# --- SSH: public 2222 -> 192.168.1.10:22 ---
frontend ft_ssh
    mode tcp
    bind :2222
    option tcplog
    option clitcpka
    timeout client 1h              # sesi SSH idle jangan diputus 30 detik
    acl src_dipercaya src 198.51.100.0/24
    tcp-request connection reject unless src_dipercaya
    default_backend bk_ssh

backend bk_ssh
    mode tcp
    option srvtcpka
    timeout server 1h
    server ssh1 192.168.1.10:22 check

# --- Aplikasi: public 9000 -> 192.168.1.20:8080 ---
frontend ft_app
    mode tcp
    bind :9000
    option tcplog
    default_backend bk_app

backend bk_app
    mode tcp
    server app1 192.168.1.20:8080 check

# --- Database: public 5433 -> 192.168.1.40:5432 (WAJIB dibatasi) ---
frontend ft_db
    mode tcp
    bind :5433
    option tcplog
    timeout client 30m
    acl src_dipercaya src 198.51.100.0/24
    tcp-request connection reject unless src_dipercaya
    default_backend bk_db

backend bk_db
    mode tcp
    timeout server 30m
    server db1 192.168.1.40:5432 check

Penjelasan singkat:

  • tcp-request connection reject unless src_dipercaya: koneksi dari IP di luar daftar langsung ditolak di level TCP, sebelum diteruskan ke backend.
  • timeout client/server 1h pada SSH: tanpa ini, timeout 30 detik di defaults akan memutus sesi SSH yang idle.
  • option clitcpka / option srvtcpka mengaktifkan TCP keep-alive di sisi klien/server.
  • Alternatif yang lebih aman: bind 10.8.0.1:5433 (IP interface VPN) sehingga port tidak terbuka di IP publik sama sekali.
  • Efek samping SSH lewat proxy: sshd dan alat seperti fail2ban di backend melihat IP HAProxy sebagai sumber koneksi, bukan IP klien. Percobaan login gagal dari satu klien bisa membuat HAProxy terblokir sehingga semua orang terkunci. Batasi di HAProxy/firewall, jangan hanya mengandalkan fail2ban di backend.

Uji:

ssh -p 2222 user@203.0.113.10
curl -v http://203.0.113.10:9000/
psql -h 203.0.113.10 -p 5433 -U dbuser dbname   # hanya dari IP yang diizinkan

10. Alur aman mengubah konfigurasi#

# 1) Backup konfigurasi saat ini
sudo cp -a /etc/haproxy/haproxy.cfg /etc/haproxy/haproxy.cfg.bak-$(date +%F-%H%M)

# 2) Edit
sudo nano /etc/haproxy/haproxy.cfg

# 3) Validasi sintaks (tidak mengubah apa pun yang sedang berjalan)
sudo haproxy -c -f /etc/haproxy/haproxy.cfg
echo "exit code: $?"      # 0 = valid

4) Baca hasil validasi.

  • Valid: Configuration file is valid, exit code 0.

  • Error: pesan [ALERT] menyebut sebab dan (biasanya) nomor baris, exit code 1. Contoh pesan nyata (HAProxy 2.8.16, teksnya bisa sedikit berbeda di versi lain):

    • unknown option 'forwardfro'; did you mean 'forwardfor' maybe ? (typo direktif)
    • Proxy 'ft_web': unable to find required default_backend: 'bk_x' (nama backend tidak ada)
    • http frontend '...' tries to use incompatible tcp backend '...' as its default backend (see 'mode') (mode frontend dan backend berbeda)
    • unable to stat SSL certificate from file '...' : No such file or directory (file sertifikat tidak ada)
    • no SSL certificate specified for bind ':443' (direktori crt kosong)

    Perbaiki baris yang ditunjuk lalu ulangi langkah 3.

  • [WARNING] tidak selalu menghentikan konfigurasi, tetapi baca dan pahami; jangan diabaikan begitu saja.

  • haproxy -c tidak memeriksa apakah port masih bebas, backend hidup, atau DNS benar. Itu ditangani langkah 6–7.

# 5) Reload hanya jika validasi sukses
sudo haproxy -c -f /etc/haproxy/haproxy.cfg && sudo systemctl reload haproxy
echo "exit code: $?"

# 6) Verifikasi HAProxy sehat. JANGAN anggap reload berhasil sebelum dicek
sudo systemctl is-active haproxy        # harus: active
sudo systemctl status haproxy --no-pager
sudo journalctl -u haproxy -n 30 --no-pager   # cari ALERT/ERROR setelah reload
sudo ss -lntp | grep haproxy            # port yang diharapkan masih LISTEN

# 7) Uji aturan yang baru diubah
curl -v -H "Host: app.example.com" http://203.0.113.10/

8) Rollback jika bermasalah:

ls -1t /etc/haproxy/haproxy.cfg.bak-* | head -3     # pilih backup terakhir
sudo cp -a /etc/haproxy/haproxy.cfg.bak-YYYY-MM-DD-HHMM /etc/haproxy/haproxy.cfg
sudo haproxy -c -f /etc/haproxy/haproxy.cfg && sudo systemctl reload haproxy
sudo systemctl status haproxy --no-pager

Reload vs restart#

reload restart
Proses Worker baru dengan konfigurasi baru dibuat; worker lama menyelesaikan koneksi yang sedang berjalan lalu berhenti Semua proses dihentikan lalu dijalankan ulang
Koneksi aktif Tidak diputus paksa Terputus
Jika konfigurasi salah Reload gagal dan worker lama tetap melayani Service bisa gagal start dan layanan mati
Pakai untuk Hampir semua perubahan konfigurasi Mengubah unit systemd/opsi startup, atau saat reload tidak efektif

Pada paket Debian/Ubuntu, unit systemd menjalankan haproxy -c lebih dulu saat reload, sehingga konfigurasi yang rusak ditolak sebelum sinyal dikirim. Anda bisa memeriksa sendiri dengan systemctl cat haproxy. Tetap validasi manual dan cek hasilnya, karena systemctl reload hanya memberi tahu bahwa perintahnya selesai, bukan bahwa seluruh aplikasi Anda sehat.


11. Checklist jaringan & firewall#

Yang diperiksa Perintah Artinya
DNS menunjuk ke IP publik yang benar dig +short app.example.com (atau host app.example.com) Harus menampilkan IP publik HAProxy.
HAProxy mendengarkan di port yang benar sudo ss -lntp | grep haproxy Daftar port LISTEN milik proses haproxy.
Tidak ada proses lain di port yang sama sudo ss -lntp 'sport = :80' Menunjukkan proses pemilik port 80. Jika bukan haproxy, itu penyebab bentrok.
Log service sudo journalctl -u haproxy -n 100 --no-pager 100 baris log terakhir (error start/reload).
Backend terjangkau dari HAProxy nc -vz 192.168.1.20 8080 Tes TCP connect ke backend, dijalankan di server HAProxy.
Aplikasi backend menjawab curl -v http://192.168.1.20:8080/ Tes HTTP langsung ke backend, dari server HAProxy.
Backend mendengarkan interface yang benar di server backend: sudo ss -lntp | grep 8080 127.0.0.1:8080 berarti hanya lokal, tidak bisa dicapai HAProxy. Butuh 0.0.0.0:8080 atau IP LAN-nya.
Respons lewat HAProxy dengan domain curl -v http://app.example.com Tes end-to-end (butuh DNS benar).
Tes tanpa menunggu DNS curl -v --resolve app.example.com:80:203.0.113.10 http://app.example.com/ Memaksa domain ke IP tertentu.
Firewall di server sudo ufw status verbose atau sudo nft list ruleset / sudo iptables -S Pastikan port 80/443/port lain diizinkan.
Buka port di UFW sudo ufw allow 80/tcp dan sudo ufw allow 443/tcp Mengizinkan HTTP/HTTPS masuk.
Tes dari luar nc -vz 203.0.113.10 443 (dari komputer lain) Port benar-benar terbuka dari internet.

Hal lain yang perlu dipastikan:

  • HAProxy di belakang router/NAT: router harus punya port forwarding dari port publik (80/443/...) ke IP LAN HAProxy.
  • Cloud (AWS/GCP/Azure/VPS): security group / cloud firewall juga harus mengizinkan port tersebut, terpisah dari firewall OS.
  • Beberapa ISP/penyedia memblokir port tertentu (misalnya 25, kadang 80/443 di jaringan rumah).

12. Error umum & troubleshooting#

Satu gejala sering punya beberapa kemungkinan penyebab. Periksa semuanya, jangan berhenti di tebakan pertama.

Gejala Kemungkinan penyebab Perintah diagnosis Solusi
HAProxy gagal start (a) Error sintaks; (b) port sudah dipakai; (c) file sertifikat tidak ada/izin salah; (d) bind ke IP yang bukan milik server; (e) direktori/izin runtime bermasalah sudo systemctl status haproxy -l, sudo journalctl -xeu haproxy, sudo haproxy -c -f /etc/haproxy/haproxy.cfg Perbaiki sesuai pesan; cek tiap kemungkinan di baris berikut.
Syntax error Typo direktori; backend yang dirujuk tidak ada; mode frontend dan backend berbeda; kurung/ACL salah sudo haproxy -c -f /etc/haproxy/haproxy.cfg Buka baris yang ditunjuk [ALERT]. Pastikan nama backend sama persis dan mode sepadan. Kembalikan backup jika perlu.
Port already in use (cannot bind socket) Nginx/Apache/proses lain memakai port itu; instance HAProxy lain; dua bind sama di file sudo ss -lntp 'sport = :80' Hentikan/pindahkan proses lain (sudo systemctl stop nginx) atau ganti port. Hapus bind ganda.
Connection refused HAProxy tidak berjalan atau tidak listen di port itu; firewall menolak (REJECT); backend tidak listen; salah port sudo ss -lntp | grep haproxy, nc -vz IP PORT, sudo ufw status Jalankan HAProxy, buka port, perbaiki port di bind/server.
HTTP 503 Tidak ada backend terpilih (use_backend/default_backend kosong); semua server di backend DOWN (health check gagal); backend tanpa server sudo journalctl -u haproxy -n 50 --no-pager atau sudo tail /var/log/haproxy.log (cari NOSRV, no server available), nc -vz IP PORT Tambahkan default_backend; perbaiki koneksi/health check ke backend; pastikan aplikasi hidup dan port benar.
HTTP 502 Backend menutup koneksi tiba-tiba atau mengirim respons tidak valid; backend memakai HTTPS tetapi HAProxy mengirim HTTP biasa (atau sebaliknya); aplikasi crash saat memproses curl -v http://IP_BACKEND:PORT/ dari server HAProxy, log aplikasi backend, sudo tail /var/log/haproxy.log Samakan protokol (tambahkan ssl di baris server jika backend HTTPS, dengan verifikasi sertifikat); perbaiki aplikasi.
Backend timeout (504) Aplikasi lambat; timeout server terlalu kecil; backend tertahan firewall (paket dibuang, bukan ditolak); backend overload curl -v --max-time 60 http://IP_BACKEND:PORT/, sudo tail /var/log/haproxy.log Perbaiki performa aplikasi atau naikkan timeout server secara wajar; cek firewall antara HAProxy dan backend.
Domain masuk ke backend yang salah ACL tidak cocok (typo, perbedaan huruf, Host memuat port); urutan use_backend terlalu longgar; domain jatuh ke default_backend; ACL HTTP dipakai di mode tcp curl -i -H "Host: app.example.com" http://IP_HAPROXY/, tinjau grep -n -E "acl|use_backend" /etc/haproxy/haproxy.cfg Perbaiki ACL (hdr(host),field(1,:) -i ...), cek urutan dan nama backend, pastikan frontend mode http.
DNS menunjuk ke IP yang salah Record A masih IP lama; belum propagasi; ada record AAAA (IPv6) lain; memakai IP backend, bukan IP HAProxy dig +short app.example.com, dig +short AAAA app.example.com, dig @8.8.8.8 +short app.example.com Perbaiki record, tunggu TTL, pastikan A dan AAAA benar. Uji dulu dengan curl --resolve.
Error sertifikat HTTPS Sertifikat kedaluwarsa; nama domain tidak ada di SAN; chain (intermediate) tidak lengkap; PEM salah urut/tanpa private key; sertifikat domain itu tidak dimuat sehingga yang tampil sertifikat lain; jam server salah openssl s_client -connect IP:443 -servername app.example.com </dev/null 2>/dev/null | openssl x509 -noout -subject -issuer -dates -ext subjectAltName, haproxy -c -f ... Terbitkan/perbarui sertifikat, gunakan fullchain.pem + key, reload, cek timedatectl. Jangan menonaktifkan verifikasi TLS.
Backend bisa diakses lokal tapi tidak dari HAProxy Aplikasi hanya bind ke 127.0.0.1; firewall backend memblokir IP HAProxy; salah subnet/routing; security group cloud di backend: sudo ss -lntp | grep PORT; dari HAProxy: nc -vz IP PORT, ip route get IP_BACKEND Ubah aplikasi agar listen di 0.0.0.0/IP LAN; izinkan IP HAProxy di firewall backend; perbaiki routing.
Firewall memblokir traffic UFW/nftables/iptables memblokir port; security group cloud; ISP; NAT belum ada sudo ufw status verbose, sudo nft list ruleset, nc -vz IP_PUBLIK PORT dari luar Izinkan port yang diperlukan (sudo ufw allow 443/tcp), cek firewall cloud dan port forwarding router.

13. Quick reference#

Perintah umum#

Tujuan Perintah
Versi / fitur build haproxy -v / haproxy -vv
Validasi konfigurasi sudo haproxy -c -f /etc/haproxy/haproxy.cfg
Reload sudo systemctl reload haproxy
Restart sudo systemctl restart haproxy
Status sudo systemctl status haproxy
Log service sudo journalctl -u haproxy -n 100 --no-pager
Log HAProxy (via rsyslog) sudo tail -f /var/log/haproxy.log
Port yang didengarkan sudo ss -lntp
Lihat unit systemd systemctl cat haproxy

/var/log/haproxy.log tersedia jika rsyslog terpasang dan paket menyediakan konfigurasinya (/etc/rsyslog.d/49-haproxy.conf). Jika tidak ada, gunakan journalctl.

Direktif yang sering dipakai#

Direktif Letak Fungsi
mode http / mode tcp defaults, frontend, backend Mode protokol
bind :PORT frontend Port yang didengarkan
bind :443 ssl crt PATH frontend HTTPS (TLS termination)
default_backend NAMA frontend Backend bawaan
use_backend NAMA if ACL frontend Pilih backend bersyarat
acl NAMA kondisi frontend, backend Definisikan kondisi
server NAMA IP:PORT check backend Server tujuan + health check
option forwardfor defaults, frontend, backend Tambah header X-Forwarded-For
option httplog / option tcplog defaults, frontend Format log
timeout connect/client/server defaults, frontend, backend Batas waktu
http-request redirect scheme https frontend Redirect HTTP ke HTTPS
http-request deny deny_status 404 frontend, backend Tolak request
tcp-request connection reject frontend Tolak koneksi di level TCP (misalnya allowlist)
tcp-request inspect-delay frontend Tunggu data untuk inspeksi TCP (SNI)

Pola frontend/backend#

# Port forwarding TCP
frontend ft_x
    mode tcp
    bind :PORT_PUBLIK
    default_backend bk_x
backend bk_x
    mode tcp
    server s1 IP_BACKEND:PORT check

# Domain routing HTTP
frontend ft_http
    mode http
    bind :80
    acl host_x hdr(host),field(1,:) -i domain.example.com
    use_backend bk_x if host_x
    default_backend bk_reject

Menambah aturan TCP forwarding baru#

  1. Backup haproxy.cfg.
  2. Tambahkan frontend dengan bind :PORT_BARU dan backend dengan server (keduanya mode tcp). Pastikan port belum dipakai (ss -lntp).
  3. Izinkan port di firewall, dan batasi sumbernya jika sensitif.
  4. sudo haproxy -c -f /etc/haproxy/haproxy.cfg && sudo systemctl reload haproxy.
  5. Cek ss -lntp, lalu uji dengan nc -vz.

Menambah domain baru#

  1. Buat record DNS ke IP publik HAProxy.
  2. Tambahkan acl + use_backend di frontend dan backend baru (Bagian 6).
  3. Untuk HTTPS: terbitkan sertifikat (tambahkan domain), buat PEM di /etc/haproxy/certs/.
  4. Validasi, reload, uji dengan curl --resolve.

Validasi & reload#

sudo haproxy -c -f /etc/haproxy/haproxy.cfg && sudo systemctl reload haproxy
sudo systemctl is-active haproxy
sudo journalctl -u haproxy -n 30 --no-pager

14. Praktik keamanan#

  • Jangan menyimpan private key atau file .pem di repositori publik (Git, dll.). Tambahkan ke .gitignore.

  • Batasi akses endpoint administratif. Socket admin: mode 660. Halaman statistik: lihat contoh di bawah.

  • Jaga HAProxy tetap terbarui (sudo apt update && sudo apt upgrade haproxy) dan pantau pengumuman keamanan.

  • Gunakan sertifikat valid untuk layanan HTTPS publik; atur ssl-min-ver TLSv1.2 (atau lebih tinggi).

  • Batasi port sensitif (SSH, database, panel admin): firewall, allowlist IP sumber, atau VPN. Jangan dibuka ke publik tanpa kontrol akses.

  • Jangan mengekspos halaman statistik HAProxy ke publik tanpa autentikasi dan pembatasan akses. Contoh yang aman (hanya localhost, akses lewat SSH tunnel):

    frontend ft_stats
        mode http
        bind 127.0.0.1:8404
        stats enable
        stats uri /stats
        stats refresh 10s
        stats auth admin:GANTI_DENGAN_PASSWORD_KUAT
    

    Akses: ssh -L 8404:127.0.0.1:8404 user@server lalu buka http://127.0.0.1:8404/stats.

  • Jangan menonaktifkan verifikasi TLS sebagai solusi umum. Jika HAProxy berbicara TLS ke backend, gunakan ssl verify required ca-file /path/ca.pem pada baris server. verify none hanya untuk uji sementara dan sadar risikonya.

  • Hindari membocorkan informasi internal ke klien: jangan menampilkan IP/versi server internal di pesan error; sembunyikan header yang tidak perlu, misalnya di backend/frontend HTTP:

        http-response del-header Server
        http-response del-header X-Powered-By
    
  • Permission file konfigurasi: /etc/haproxy/haproxy.cfg dan /etc/haproxy/certs/ sebaiknya hanya bisa dibaca root (atau grup khusus).


15. Referensi resmi#

Topik Tautan
Frontends https://www.haproxy.com/documentation/haproxy-configuration-tutorials/proxying-essentials/configuration-basics/frontends/
Backends https://www.haproxy.com/documentation/haproxy-configuration-tutorials/proxying-essentials/configuration-basics/backends/
HTTP proxying https://www.haproxy.com/documentation/haproxy-configuration-tutorials/protocol-support/http/
TCP proxying https://www.haproxy.com/documentation/haproxy-configuration-tutorials/protocol-support/tcp/
Configuration Manual (pilih versi Anda) https://docs.haproxy.org/

Direktif dan sample fetch yang dipakai dokumen ini (bind, use_backend, acl, hdr, field, req.ssl_sni, req.ssl_hello_type, tcp-request, http-request, timeout, option forwardfor) terdapat di Configuration Manual. Untuk versi spesifik Anda, buka bagian yang sesuai dengan keluaran haproxy -v.

Back to library

Keyboard shortcuts

/
Focus search
Esc
Close dialogs, menus and messages
?
Show this help
Ctrl S
Save in the admin editor