How I Built a Real-Time Domain Block Checker for Indonesian ISPs
If you run a website targeting Indonesian users, you've probably heard of TrustPositif — the government's domain blocking system managed by Komdigi (Ministry of Communication). Over 1 million domains are blocked, and ISPs like IndiHome, Telkomsel, Biznet, First Media, and MyRepublic are legally required to enforce it.
The worst part? Your domain can get blocked without any notification.
I built SafeDomain to solve this — a free tool that checks your domain against TrustPositif AND 5 major ISPs simultaneously, in real-time.
The Problem
Indonesian ISPs block domains at the DNS level. When a domain is flagged:
- User's browser sends DNS request to ISP resolver
- ISP checks against TrustPositif database
- If blocked → redirected to ISP's block page
- If not → domain loads normally
The tricky part is that each ISP has different DNS resolvers and sometimes blocks domains at different times. IndiHome might block a domain before Telkomsel does.
The Tech Stack
- Flask — lightweight Python web framework
- SQLite — caching layer (WAL mode for concurrent reads)
- dnspython — DNS resolution per ISP
- requests — TrustPositif API calls
- Gunicorn — WSGI server behind Nginx
- Cloudflare — CDN + DDoS protection
How the Domain Check Works
import dns.resolver
import requests
ISP_RESOLVERS = {
"IndiHome": "118.98.44.10",
"Telkomsel": "198.0.0.1",
"Biznet": "180.131.144.144",
"First Media": "103.12.160.2",
"MyRepublic": "202.152.2.2",
}
INDIHOME_BLOCK_IP = "36.86.63.182" # IndiHome's block redirect IP
def check_isp_block(domain: str) -> dict:
results = {}
for isp, resolver_ip in ISP_RESOLVERS.items():
try:
resolver = dns.resolver.Resolver()
resolver.nameservers = [resolver_ip]
resolver.lifetime = 3.0
answers = resolver.resolve(domain, 'A')
resolved_ip = str(answers[0])
results[isp] = "BLOCKED" if resolved_ip == INDIHOME_BLOCK_IP else "SAFE"
except dns.exception.DNSException:
results[isp] = "ERROR"
return results
def check_trustpositif(domain: str) -> str:
try:
resp = requests.get(
f"https://trustpositif.komdigi.go.id/api/check?domain={domain}",
timeout=5
)
data = resp.json()
return "BLOCKED" if data.get("status") == "blocked" else "SAFE"
except:
return "ERROR"
Caching Strategy
Running live DNS checks on every request would be slow. I implemented a SQLite cache with 1-hour TTL:
import sqlite3
from datetime import datetime, timedelta
def get_cache(domain: str):
conn = sqlite3.connect("cache.db")
row = conn.execute(
"SELECT status, checked_at, detail FROM cache WHERE domain = ?",
(domain,)
).fetchone()
conn.close()
if row:
checked_at = datetime.strptime(row[1], "%Y-%m-%d %H:%M:%S")
age = (datetime.now() - checked_at).total_seconds()
if age < 3600: # 1 hour TTL
return row
return None
def set_cache(domain: str, status: str, detail: str = ""):
conn = sqlite3.connect("cache.db")
conn.execute(
"""INSERT OR REPLACE INTO cache (domain, status, checked_at, detail)
VALUES (?, ?, datetime('now'), ?)""",
(domain, status, detail)
)
conn.commit()
conn.close()
This brought response time from ~2s to ~150ms for cached domains.
Nginx Microcache Layer
On top of SQLite caching, I added Nginx proxy caching for 10 minutes:
proxy_cache_path /tmp/nginx_cache levels=1:2
keys_zone=cek_cache:10m max_size=100m
inactive=10m use_temp_path=off;
location ~* ^/cek/ {
proxy_cache cek_cache;
proxy_cache_valid 200 10m;
proxy_cache_use_stale error timeout updating;
proxy_pass http://127.0.0.1:5003;
}
Result: 0.65s → 0.15s on repeated requests.
SEO for a Domain Checker Tool
Since the tool checks 300+ popular Indonesian domains, each /cek/[domain] page is indexed by Google with dynamic meta tags:
@app.route("/cek/<path:domain>")
def cek(domain):
# Dynamic meta tags per domain
return render_template("cek.html",
domain=domain,
title=f"Cek {domain} — Diblokir TrustPositif?",
description=f"Cek apakah {domain} diblokir TrustPositif Komdigi dan 5 ISP Indonesia."
)
Combined with FAQ JSON-LD schema, each domain page becomes a potential long-tail SEO target.
Lessons Learned
1. ISP DNS resolvers are flaky
Some Indonesian ISP resolvers have high latency or drop packets. Always set a short timeout (3s) and handle errors gracefully.
2. TrustPositif API is rate-limited
The official API can be slow. Cache aggressively.
3. Domain blocking is asynchronous across ISPs
IndiHome might block a domain hours before Telkomsel. Always check all ISPs independently.
4. Users want simplicity
The most popular feature is the simple "Is it blocked or not?" indicator, not the detailed per-ISP breakdown.
What's Next
- Telegram bot for automated monitoring with instant notifications
- SmartRedirector — auto-rotate to backup domains when primary is blocked
- API endpoint for developers to integrate into their own monitoring
Try It
🔍 Free domain checker: cek.opensite.site
📊 Full monitoring dashboard: safedomain.id
Built with Flask, SQLite, and a lot of frustration with Indonesian internet censorship 😅
Have you dealt with domain blocking in your country? I'd love to hear how others handle this problem.
Top comments (0)