Bayangkan gedung parkir berlangganan. Begitu masa langganan habis, palang pintu keluar tidak mau terbuka. Namun, petugas tidak mengusir mobil Anda dari gedung. Ia justru menunjuk loket pembayaran di pojok. Isolir pelanggan PPPoE bekerja dengan cara yang sama: pelanggan yang menunggak tetap bisa tersambung ke router, tetapi semua jalan keluar ke internet tertutup, kecuali jalan menuju halaman pemberitahuan tagihan.
Pada tutorial ini kami membedah sistem isolir yang benar-benar berjalan di jaringan ISP kami: pool IP khusus bernama EXPIRED, firewall MikroTik, grup RADIUS, dan halaman pemberitahuan self-host. Selain itu, kami juga membahas dua hal yang jarang tertulis di tutorial lain. Pertama, jebakan Framed-IP yang membuat pelanggan terisolir tetap bisa menonton YouTube. Kedua, alasan auto-redirect ke halaman isolir sering tidak muncul di HP modern, padahal blokirnya tetap jalan. Kami mengganti IP, domain, dan nama pelanggan dengan nilai contoh, lalu menyensor screenshot asli.

Cara Kerja Isolir Pelanggan PPPoE dengan RADIUS
Cara lama yang sering kita temui adalah menolak login pelanggan (Auth-Type := Reject). Hasilnya memang tegas, tetapi pelanggan hanya melihat modem gagal tersambung. Akibatnya, mereka menelepon teknisi dan mengira jaringan rusak. Oleh karena itu, kami memilih pola yang lebih ramah: pelanggan tetap login, tetapi masuk ke “ruang tunggu”.
Ruang tunggu ini berupa subnet khusus 10.200.200.0/23. Setiap IP dari subnet tersebut otomatis masuk address-list EXPIRED, sehingga firewall cukup mencocokkan satu daftar. Dengan kata lain, kita tidak perlu menulis rule per pelanggan. Berikut pembagian tugas setiap komponen.
| Komponen | Tugas | Contoh nilai |
|---|---|---|
| Billing | Memindahkan pelanggan ke grup isolir lalu memutus sesinya | tombol Isolir atau cron jatuh tempo |
| FreeRADIUS | Membalas login dengan profil dan pool isolir | grup isolir, priority 1 |
| MikroTik pool + profile | Memberi IP dari subnet isolir | 10.200.200.2-10.200.201.254 |
| Firewall filter | Membuang semua trafik kecuali DNS dan halaman isolir | drop TCP dan UDP |
| NAT dst-nat | Membelokkan HTTP port 80 ke server halaman isolir | 203.0.113.10:80 |
| Nginx | Menyajikan halaman pemberitahuan untuk Host apa pun | isolir.contoh.com |
Langkah 1: Grup Isolir di FreeRADIUS
Pertama, kita membuat grup isolir di tabel radgroupreply. Grup ini cukup berisi dua atribut, dan kita hanya perlu membuatnya sekali.
INSERT INTO radgroupreply (groupname, attribute, op, value) VALUES
('isolir', 'Mikrotik-Group', ':=', 'EXPIRED_ISOLIR'),
('isolir', 'Framed-Pool', ':=', 'EXPIRED_ISOLIR');
Mikrotik-Group menyuruh MikroTik memakai PPP profile EXPIRED_ISOLIR. Sementara itu, Framed-Pool menyuruh router mengambil IP dari pool dengan nama yang sama. Atribut kedua inilah yang paling penting. Tanpa Framed-Pool, router tetap memberi IP dari pool bawaan profil lama, sehingga pelanggan tidak pernah masuk subnet isolir.
Memindahkan Pelanggan ke Grup Isolir
Selanjutnya, untuk mengisolir pelanggan budi, kita turunkan prioritas grup paketnya lalu tambahkan grup isolir dengan prioritas 1.
UPDATE radusergroup SET priority = 10 WHERE username = 'budi' AND groupname <> 'isolir';
DELETE FROM radusergroup WHERE username = 'budi' AND groupname = 'isolir';
INSERT INTO radusergroup (username, groupname, priority) VALUES ('budi', 'isolir', 1);
Langkah menurunkan prioritas ke 10 bukan hiasan. Jika grup paket dan grup isolir sama-sama berprioritas 1, urutan pembacaan FreeRADIUS menjadi tidak pasti. Akibatnya, kadang paket asli yang menang. Kemudian, baris DELETE membuat perintah ini aman untuk berjalan berulang kali tanpa menghasilkan baris ganda. Untuk memulihkan, kita cukup menghapus baris isolir dan mengembalikan prioritas grup paket ke 1.
Langkah 2: Pool dan PPP Profile EXPIRED di MikroTik
Berikutnya, kita menyiapkan pasangan dari grup RADIUS tadi di router. Nama pool dan profile harus sama persis dengan nilai atribut di FreeRADIUS. Di router kami namanya EXPIRED_TBRADIUS, sedangkan contoh di artikel ini memakai EXPIRED_ISOLIR.
:if ([:len [/ip pool find name=EXPIRED_ISOLIR]] = 0) do={/ip pool add name=EXPIRED_ISOLIR ranges=10.200.200.2-10.200.201.254 comment="Pool Isolir"}
:if ([:len [/ppp profile find name=EXPIRED_ISOLIR]] = 0) do={/ppp profile add name=EXPIRED_ISOLIR local-address=10.200.200.1 remote-address=EXPIRED_ISOLIR dns-server=8.8.8.8,1.1.1.1 comment="Profile Isolir"}
Setiap perintah sengaja kami tulis dalam satu baris. Versi lama skrip kami memakai garis miring terbalik untuk menyambung baris, dan hasilnya sering pecah saat admin menempelkannya ke terminal RouterOS. Selain itu, pembungkus :if ... find membuat skrip tetap aman walaupun Anda jalankan berulang kali. Pool berisi 509 IP, jadi cukup untuk ratusan pelanggan menunggak sekaligus. Terakhir, router kami juga memasang rate-limit=1k/1k pada profil ini sebagai pengaman tambahan jika suatu saat rule firewall terhapus.
Langkah 3: Address-List dan Firewall Drop
Setelah pool siap, kita mendaftarkan subnet isolir dan alamat yang boleh diakses ke address-list. EXPIRED_PAGE berisi gateway isolir dan IP server halaman pemberitahuan.
/ip firewall address-list add list=EXPIRED address=10.200.200.0/23 comment="subnet pool isolir"
/ip firewall address-list add list=EXPIRED_PAGE address=10.200.200.1 comment="gateway isolir"
/ip firewall address-list add list=EXPIRED_PAGE address=203.0.113.10 comment="server halaman isolir"
/ip firewall filter add chain=forward action=drop src-address-list=EXPIRED dst-address-list=!EXPIRED_PAGE protocol=udp dst-port=!53 comment="drop isolir UDP non-DNS"
:do {/ip firewall filter move [find comment="drop isolir UDP non-DNS"] destination=0} on-error={}
/ip firewall filter add chain=forward action=drop src-address-list=EXPIRED dst-address-list=!EXPIRED_PAGE protocol=tcp dst-port=!53,80 comment="drop isolir TCP non-page"
:do {/ip firewall filter move [find comment="drop isolir TCP non-page"] destination=0} on-error={}
Ada tiga hal yang perlu Anda perhatikan. Pertama, kita membuang TCP dan UDP. Banyak aplikasi seperti YouTube memakai QUIC lewat UDP, jadi blokir TCP saja tidak cukup. Kedua, port 80 sengaja lolos agar NAT di langkah berikutnya sempat membelokkannya. Ketiga, perintah move destination=0 menaruh rule di urutan teratas. Jika router punya rule accept established,related, rule drop harus berada di atasnya. Kalau tidak, video yang sudah terbuka sebelum isolir tetap berjalan.
Langkah 4: NAT Redirect HTTP dan DNS Hijack
Firewall sudah menutup jalan keluar. Sekarang kita menunjuk “loket pembayaran” lewat dua rule NAT dan satu pengaturan DNS.
/ip firewall nat add chain=dstnat action=dst-nat src-address-list=EXPIRED dst-address-list=!EXPIRED_PAGE protocol=tcp dst-port=80 to-addresses=203.0.113.10 to-ports=80 comment="redirect HTTP ke isolir page" /ip firewall nat add chain=dstnat action=redirect src-address-list=EXPIRED protocol=udp dst-port=53 to-ports=53 comment="DNS hijack" /ip dns set servers=8.8.8.8,1.1.1.1 allow-remote-requests=yes /ip dns static add name=isolir.contoh.com address=203.0.113.10 comment="isolir page domain" /ip proxy set enabled=no
Rule pertama membelokkan setiap permintaan HTTP dari subnet isolir ke server halaman pemberitahuan. Rule kedua menangkap query DNS ke server mana pun, lalu menjawabnya dari resolver MikroTik. Cara ini penting karena banyak modem pelanggan tidak memakai DNS dari profil PPP. Kemudian, static DNS hanya berisi domain milik kita sendiri yang menunjuk ke IP aslinya, sehingga aman bagi pelanggan aktif.
Perhatikan juga baris terakhir. Tutorial lama memakai web-proxy dengan redirect-to, padahal RouterOS v7 sudah menghapus parameter itu. Jika proxy terlanjur aktif tetapi rule-nya gagal terbentuk, web-proxy justru meneruskan trafik pelanggan isolir. Oleh karena itu, kami mematikan proxy dan memakai dst-nat yang berjalan di RouterOS v6 maupun v7.

Gotcha Framed-IP: IP Statis Mengalahkan Framed-Pool
Bagian ini menyita waktu kami paling banyak. Sistem billing kami memberi setiap pelanggan IP tetap lewat atribut Framed-IP-Address di tabel radreply. Masalahnya, MikroTik mendahulukan IP statis tersebut daripada Framed-Pool. Jadi, walaupun grup isolir sudah menang, pelanggan tetap mendapat IP subnet normal. Akibatnya, IP itu tidak cocok dengan address-list EXPIRED, dan semua rule firewall terlewati begitu saja.
Karena itu, proses isolir wajib menghapus IP statis, lalu proses pemulihan menulisnya kembali.
-- saat isolir
DELETE FROM radreply WHERE username = 'budi' AND attribute = 'Framed-IP-Address';
-- saat pulih (IP diambil dari data akun di billing)
INSERT INTO radreply (username, attribute, op, value) VALUES ('budi', 'Framed-IP-Address', '=', '10.3.0.74');
Namun, menghapus di satu tempat saja belum cukup. Kami menemukan dua jalur lain yang diam-diam menulis ulang IP statis: fungsi sinkronisasi saat admin mengedit akun, dan fungsi yang membangun data akun tanpa membawa kolom status. Dengan demikian, aturan finalnya ada di tiga tempat: isolir menghapus, pemulihan menambah, dan sinkronisasi hanya menulis IP untuk akun berstatus aktif.
Query Audit yang Wajib Kosong
Selanjutnya, jalankan query berikut secara berkala. Hasil yang benar adalah kosong.
SELECT rug.username, rr.value AS ip_statis FROM radusergroup rug JOIN radreply rr ON rr.username = rug.username AND rr.attribute = 'Framed-IP-Address' WHERE rug.groupname = 'isolir';
Kami menguji seluruh SQL di atas pada MariaDB server uji. Sebelum perbaikan, query audit menampilkan budi | 10.3.0.74. Sesudah DELETE, hasilnya kosong. Kemudian, setelah pemulihan, grup paket kembali ke prioritas 1 dan IP statis muncul lagi.
Contoh Kasus: Terisolir Dua Hari, Tetap Online
Seorang pelanggan berstatus isolir selama dua hari, tetapi sesinya masih memakai IP 10.3.0.74 dari subnet normal. Ia tetap bisa memakai internet penuh. Grup isolir di radusergroup ternyata sudah benar, sehingga pemeriksaan sekilas tampak beres. Penyebab sebenarnya adalah baris Framed-IP-Address yang masih tersisa. Setelah kami menghapus baris itu dan memutus sesinya, modem pelanggan langsung mendapat IP 10.200.x.
Pelajaran kedua datang seminggu kemudian. Laporan serupa masuk, tetapi kali ini /ppp active di router sudah menunjukkan IP 10.200.201.x. Ternyata yang salah hanya tampilan billing, karena paket Accounting-Start sesi baru tidak sampai ke RADIUS. Jadi, selalu cek tiga hal berurutan: grup isolir, baris Framed-IP, lalu IP sesi di router.

Screenshot di atas kami ambil saat menulis artikel ini. Dari sepuluh pelanggan isolir yang sedang online, sembilan sudah berada di pool 10.200.x. Satu sesi masih memakai IP subnet normal dan memang punya baris Framed-IP tersisa. Selain itu, audit menemukan 22 baris Framed-IP basi pada akun isolir yang sedang offline. Inilah alasan query audit harus berjalan rutin, bukan sekali saja.
Halaman Isolir Self-Host dengan Nginx
Setelah urusan router beres, kita butuh server yang menjawab semua permintaan HTTP yang terbelokkan. Kuncinya adalah blok default_server dengan server_name _. Pelanggan bisa membuka situs apa saja, dan dst-nat tidak mengubah header Host. Oleh karena itu, server harus menyajikan halaman isolir untuk Host apa pun.
server {
listen 80 default_server;
server_name _;
root /var/www/isolir;
index index.html;
add_header Cache-Control "no-store, no-cache, must-revalidate, max-age=0" always;
etag off;
location = /generate_204 { return 302 https://isolir.contoh.com/; }
location = /hotspot-detect.html { return 302 https://isolir.contoh.com/; }
location = /connecttest.txt { return 302 https://isolir.contoh.com/; }
location = /canonical.html { return 302 https://isolir.contoh.com/; }
location / { try_files $uri $uri/ /index.html; }
location ~ /\. { deny all; }
}
Empat location di tengah menangani probe captive portal milik Android, iOS, Windows, dan Firefox. Sistem operasi mengharapkan jawaban tertentu, misalnya kode 204. Karena kita membalas 302, HP menganggap jaringan butuh login dan menampilkan notifikasi. Sementara itu, header no-store mencegah browser menyimpan halaman isolir, sehingga pelanggan yang sudah membayar tidak melihat halaman lama dari cache.
Uji dengan curl
Kami menjalankan konfigurasi ini sebagai instance Nginx terpisah di server uji pada port 28417. Selain port dan folder root, isinya sama persis.
$ curl -s -o /dev/null -w "%{http_code} %{redirect_url}\n" -H "Host: connectivitycheck.gstatic.com" http://127.0.0.1:28417/generate_204
302 https://isolir.contoh.com/
$ curl -s -o /dev/null -w "%{http_code} %{redirect_url}\n" -H "Host: captive.apple.com" http://127.0.0.1:28417/hotspot-detect.html
302 https://isolir.contoh.com/
$ curl -s -H "Host: berita-acak.contoh.net" http://127.0.0.1:28417/artikel/123 | grep -o "<h1>.*</h1>"
<h1>Akun Anda Terisolir</h1>
$ curl -sI http://127.0.0.1:28417/ | grep -i cache-control
Cache-Control: no-store, no-cache, must-revalidate, max-age=0
Hasilnya sesuai harapan. Probe Android dan iOS mendapat 302, kemudian domain acak tetap menerima halaman isolir. Header anti-cache juga sudah terpasang.
Wajib HTTPS untuk Halaman Pemberitahuan
Awalnya kami menyajikan halaman isolir lewat HTTP biasa. Tidak lama kemudian, pelanggan melaporkan iklan game dan dompet digital muncul di atas halaman kami. Iklan itu berasal dari perantara di jalur jaringan seluler yang menyisipkan script ke halaman HTTP. Sejak itu, port 80 hanya bertugas membelokkan, sedangkan isi halaman tampil lewat HTTPS.
server {
listen 443 ssl;
server_name isolir.contoh.com;
root /var/www/isolir.contoh.com;
index index.html;
ssl_certificate /etc/letsencrypt/live/isolir.contoh.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/isolir.contoh.com/privkey.pem;
add_header Cache-Control "no-store, no-cache, must-revalidate" always;
location / { try_files $uri $uri/ /index.html; }
location ~ /\. { deny all; }
}
Jika Anda memasang halaman di domain sendiri, tambahkan IP hosting ke EXPIRED_PAGE dan buat static DNS untuk domain tersebut di MikroTik. Tanpa entri address-list, firewall akan membuang koneksi HTTPS ke halaman Anda sendiri. Selain itu, sisakan location ^~ /.well-known/acme-challenge/ di blok port 80 agar perpanjangan sertifikat Let’s Encrypt tetap jalan.

Kenapa Redirect Isolir Tidak Muncul di HP Modern
Sekarang bagian yang paling sering membuat admin ISP bingung. Semua konfigurasi sudah benar, pelanggan memang tidak bisa internet, tetapi halaman isolir tidak pernah muncul. Ternyata penyebabnya bukan router, melainkan cara HP modern mengakses internet.

Pertama, hampir semua situs dan aplikasi kini memakai HTTPS. Router tidak bisa membelokkan HTTPS tanpa sertifikat yang cocok, jadi permintaan tersebut berakhir di rule drop. Kedua, Chrome dan Android memakai DNS over HTTPS (DoH) ke 8.8.8.8 atau 1.1.1.1 lewat TCP 443. Karena rule drop juga memotong jalur itu, browser gagal mencari nama domain. Akibatnya, browser bahkan tidak pernah mengirim permintaan HTTP yang bisa kita belokkan. Pelanggan hanya melihat pesan seperti DNS_PROBE_FINISHED_BAD_SECURE_CONFIG.
| Permintaan dari HP | Nasib di router | Yang pelanggan lihat |
|---|---|---|
| HTTP biasa (port 80) | dst-nat ke server isolir | Halaman isolir |
| HTTPS (port 443) | Drop | Koneksi gagal atau timeout |
| DNS over HTTPS | Drop | Error DNS di browser |
| Probe captive portal OS | dst-nat lalu 302 | Notifikasi “Login ke jaringan” yang harus pelanggan ketuk |
Ketik manual http://203.0.113.10 | Lolos ke EXPIRED_PAGE | Halaman isolir |
Contoh Kasus: 12.797 Probe, Hanya Satu Kunjungan
Log Nginx kami selama 14 hari terakhir mencatat 12.797 probe captive portal dari HP dan laptop pelanggan isolir. Sebanyak 10.878 di antaranya adalah /generate_204 milik Android, lalu 1.315 berasal dari Windows dan 444 dari perangkat Apple. Server membalas semuanya dengan 302 ke halaman HTTPS. Namun, log HTTPS halaman isolir hanya mencatat satu kunjungan sejak akhir Agustus.
Dengan kata lain, mekanisme blokir bekerja sempurna, dan HP juga sadar sedang berada di jaringan “captive”. Akan tetapi, pelanggan jarang mengetuk notifikasi itu, sehingga pelanggan tidak pernah membaca pesannya. Kami juga sudah mencoba web-proxy, memblokir server DoH agar HP kembali ke DNS biasa, sampai mengubah DNS modem lewat TR-069. Hasilnya tetap tidak konsisten di HP modern.

Contoh Kasus: Jangan Hijack DNS Google
Ide yang terdengar cerdas adalah membuat static DNS untuk connectivitycheck.gstatic.com atau clients3.google.com agar probe Android selalu mendarat di halaman isolir. Masalahnya, static DNS MikroTik bersifat global dan tidak bisa kita batasi per address-list. Akibatnya, semua pengguna router ikut kena, termasuk pelanggan aktif dan admin di LAN kantor.
Kami mengalami dua insiden karenanya. Admin gagal login ke panel billing karena reCAPTCHA dari www.gstatic.com menolak sertifikat yang salah. Selain itu, HP seorang pelanggan aktif menampilkan halaman isolir padahal tagihannya lunas. Oleh karena itu, jangan pernah membuat static DNS untuk domain Google, Apple, atau Microsoft. Biarkan probe datang lewat dst-nat port 80, karena rule itu hanya berlaku untuk subnet EXPIRED.
Solusi Praktis: Kirim Link Lewat WhatsApp
Karena auto-redirect tidak bisa kita andalkan, kami memindahkan beban pemberitahuan ke saluran yang pasti terbaca. Saat sistem mengisolir pelanggan, billing kami mengirim pesan WhatsApp pemberitahuan isolir. Admin bisa menambahkan jumlah tagihan dan link pembayaran ke template pesannya. Pesan ini tetap sampai karena pelanggan biasanya masih punya kuota data seluler.
Selain itu, cantumkan IP halaman isolir dalam bentuk angka di pesan tersebut, misalnya http://203.0.113.10. Alamat berbentuk IP tidak butuh DNS, jadi tetap terbuka dari WiFi rumah yang sedang terisolir. Dengan demikian, halaman pemberitahuan berubah fungsi menjadi pelengkap, sedangkan blokir firewall tetap menjadi penegak utama.
Kesalahan Umum
- Hanya memasang
Mikrotik-GrouptanpaFramed-Pool. Profil berubah, tetapi IP tetap dari pool lama, sehingga firewall isolir tidak pernah cocok. - Membiarkan
Framed-IP-Addressdiradreply. IP statis mengalahkan pool isolir. Jalankan query audit secara rutin. - Grup paket dan grup isolir sama-sama prioritas 1. Urutannya menjadi acak, jadi turunkan grup paket ke 10.
- Lupa memutus sesi setelah isolir. Balasan RADIUS hanya berlaku saat login baru, jadi sesi lama tetap memakai IP normal sampai modem tersambung ulang.
- Hanya membuang TCP. QUIC lewat UDP membuat YouTube tetap jalan.
- Rule drop di bawah
accept established. Koneksi yang terbuka sebelum isolir tetap hidup. - Memakai web-proxy
redirect-todi RouterOS v7. Parameter itu sudah tidak ada, dan proxy yang aktif tanpa rule justru meneruskan trafik. - Static DNS untuk domain probe Google atau Apple. Pelanggan aktif ikut melihat halaman isolir dan layanan Google rusak.
- Menyajikan halaman isolir lewat HTTP saja. Perantara di jalur seluler bisa menyisipkan iklan ke halaman Anda.
Kesimpulan
Isolir pelanggan PPPoE yang andal berdiri di atas tiga lapis. FreeRADIUS memindahkan pelanggan ke grup isolir dengan Framed-Pool, lalu MikroTik memberi IP dari pool EXPIRED. Setelah itu, firewall membuang semua trafik kecuali DNS dan halaman pemberitahuan. Lapis-lapis ini bekerja tanpa bergantung pada perilaku HP pelanggan, sehingga tujuan utama isolir selalu tercapai.
Sebaliknya, auto-redirect ke halaman isolir hanyalah bonus. HTTPS dan DNS over HTTPS membuat sebagian besar HP modern tidak pernah melihatnya. Jadi, pasang halaman isolir HTTPS dengan Nginx catch-all, kirim link lewat WhatsApp, dan jalankan audit Framed-IP secara rutin. Dengan begitu, pelanggan yang menunggak benar-benar berhenti, sementara pelanggan yang sudah membayar tidak ikut terganggu.
Baca Juga
- Keamanan Server Linux: Tutorial Hardening SSH, Firewall UFW, CrowdSec, dan Alarm Telegram
- Cara Akses Proxmox dari Internet Tanpa IP Publik (WireGuard + VPS)
- Cara Membuat Dashboard Monitoring Server (NOC) dengan Uptime Kuma dan Beszel
Referensi: MikroTik Docs: Firewall Filter, MikroTik Docs: NAT, FreeRADIUS Wiki: rlm_sql, nginx: Server names, RFC 8484: DNS over HTTPS.

