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 -cpada 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#
- Konsep dasar
- Instalasi
- Dasar file konfigurasi
- Cara memakai contoh di dokumen ini
- Contoh A: port forwarding sederhana
- Contoh B: forwarding berdasarkan domain (HTTP)
- Contoh C: HTTPS dengan TLS termination
- Contoh D: HTTPS TCP passthrough
- Contoh E: beberapa port sekaligus
- Alur aman mengubah konfigurasi
- Checklist jaringan & firewall
- Error umum & troubleshooting
- Quick reference
- Praktik keamanan
- 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 cekhaproxy -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
globaldandefaults(termasukerrorfiledan 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)#
- Setiap contoh (A–E) berdiri sendiri dan berisi
global/defaults/frontend/backendlengkap supaya bisa divalidasi sendiri-sendiri. - 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
- Cara menerapkan ke server yang sudah berjalan: gabungkan hanya seksi
frontend/backendyang diperlukan ke/etc/haproxy/haproxy.cfg(setelah backup), pertahankanglobal/defaultsmilik Anda, lalu ikuti Bagian 10. Sesuaikan jugatimeoutdidefaultsAnda jika perlu. - Setiap
frontenddanbackendharus memakaimodeyang sama (keduanyahttpatau keduanyatcp). 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 port8080 - 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 akhirserver 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 tcptidak 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 frontendmode tcp. Untuk itu gunakanmode http(Contoh B).- Dua sisi harus terjangkau: (1) klien harus bisa mencapai
HAProxy:9000(firewall/NAT terbuka), dan (2) HAProxy harus bisa mencapai192.168.1.20:8080. Jika salah satu putus, koneksi gagal. checkmembuat 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:8080api.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
- Buat record DNS
blog.example.comyang 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
certbotatauacme.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 600cukup. 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-requestdijalankan sebelumuse_backend. Itu sebabnya pengecualian ACME ditulis langsung pada barisredirect(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):
- Jalankan HAProxy dengan hanya
ft_http,bk_certbot, dan backend lain (beri komentar padaft_https), lalu validasi dan reload. - Terbitkan sertifikat:
sudo certbot certonly --standalone --http-01-port 8888 \ -d app.example.com -d api.example.com - Buat file PEM dan atur izinnya (lihat 7.2).
- Aktifkan kembali
ft_https, laluhaproxy -cdan 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_sniadalah sample fetch khusus TLS yang membaca SNI dari ClientHello. Jangan memakaihdr(host)ataupath_begdi frontendmode tcp: itu ACL HTTP dan butuhmode http(di passthrough isinya terenkripsi sehingga tidak bisa dibaca). Hati-hati: pada pengujian di HAProxy 2.8.16, ACL seperti itu di frontendmode tcploloshaproxy -ctanpa 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_backendjika 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-proxydi barisserver), 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 1hpada SSH: tanpa ini, timeout 30 detik didefaultsakan memutus sesi SSH yang idle.option clitcpka/option srvtcpkamengaktifkan 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:
sshddan alat sepertifail2bandi 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 mengandalkanfail2bandi 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 code0. -
Error: pesan
[ALERT]menyebut sebab dan (biasanya) nomor baris, exit code1. 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'(direktoricrtkosong)
Perbaiki baris yang ditunjuk lalu ulangi langkah 3.
-
[WARNING]tidak selalu menghentikan konfigurasi, tetapi baca dan pahami; jangan diabaikan begitu saja. -
haproxy -ctidak 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.logtersedia jika rsyslog terpasang dan paket menyediakan konfigurasinya (/etc/rsyslog.d/49-haproxy.conf). Jika tidak ada, gunakanjournalctl.
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#
- Backup
haproxy.cfg. - Tambahkan
frontenddenganbind :PORT_BARUdanbackenddenganserver(keduanyamode tcp). Pastikan port belum dipakai (ss -lntp). - Izinkan port di firewall, dan batasi sumbernya jika sensitif.
sudo haproxy -c -f /etc/haproxy/haproxy.cfg && sudo systemctl reload haproxy.- Cek
ss -lntp, lalu uji dengannc -vz.
Menambah domain baru#
- Buat record DNS ke IP publik HAProxy.
- Tambahkan
acl+use_backenddi frontend danbackendbaru (Bagian 6). - Untuk HTTPS: terbitkan sertifikat (tambahkan domain), buat PEM di
/etc/haproxy/certs/. - 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
.pemdi 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_KUATAkses:
ssh -L 8404:127.0.0.1:8404 user@serverlalu bukahttp://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.pempada barisserver.verify nonehanya 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.cfgdan/etc/haproxy/certs/sebaiknya hanya bisa dibaca root (atau grup khusus).
15. Referensi resmi#
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.