Use connection: {accept: false} to reject a connection by hostname, before
any dial or TLS handshake — the client never receives a certificate, so it
never needs to trust mitmania's signing CA. This is the shape a hostname
blocklist wants: many entries, each meaning "refuse this host outright," not
"intercept it to inspect and then block."
Why not mitm:true + block?¶
A request-phase block action also stops the request, but only after
mitmania terminates TLS to reach the message phase — the client sees
mitmania's leaf certificate and rejects it unless the signing CA is
installed. For a large blocklist, every blocked domain then shows a
certificate error on any client that hasn't installed the CA — often none of
them, on a transparently-intercepted network.
connection: {accept: false} skips interception entirely for a match: no
dial, no TLS termination, no certificate exchange. The connection is refused
at the same point a no-match would be, just for a reason you named
explicitly.
{ "uuid": "docs-ad-blocking", "http": [ { "match": {"host": "ads.example"}, "connection": {"accept": false} }, { "match": {"host": "re:(?i)(?:^|\.)(?:tracker\.example)$"}, "connection": {"accept": false} }, { "match": {}, "mitm": false } ] }
Apply it with PUT /rules/{client-ip} (or embed the same http[] shape in a
rules/default bucket for a fleet-wide blocklist — see
The default rule table). ads.example and
tracker.example (and its subdomains) are refused outright; everything else
falls through to the trailing mitm:false splice.
Matching by hostname at scale¶
A real blocklist is many exact hostnames, not a hand-written glob. Group them
into one re: suffix pattern instead of one rule per host —
internal/rules recognizes this specific canonical shape and indexes it, so
matching stays fast even with hundreds of thousands of entries:
{"match": {"host": "re:(?i)(?:^|\\.)(?:ads\\.example|tracker\\.example)$"},
"connection": {"accept": false}}
Generate a real blocklist automatically¶
Hand-authoring one rule per hostname doesn't scale to a real blocklist.
tools/adblock_to_mitmania.py converts uBlock/AdGuard/hosts-style filter
lists into exactly the shape above — one indexed re: suffix pattern per
generated rule, connection:{accept:false} by default — so this is the
ordinary way a real fleet's blocklist gets built, not a one-off script. It
needs only Python 3's standard library; no pip install. See
the full CLI/preset reference
for every flag.
1. Generate first, push later¶
Write the generated rules to a file and inspect them before anything goes live. With no arguments the converter uses HaGeZi Multi Light (ads/trackers) plus HaGeZi TIF Mini (malware/phishing/scam/C2), pulled straight from each provider's maintained URL:
python3 tools/adblock_to_mitmania.py \
--output generated-rules.json \
--domains-output generated-domains.txt
generated-domains.txt is the flat, sorted, deduplicated hostname list —
the fastest way to spot-check what's actually being blocked before trusting
it. Progress lines report how many rules each provider contributed and the
final effective-domain/generated-rule counts.
2. Apply it fleet-wide¶
Once you're satisfied, regenerate straight against the live control socket
instead of a file. This fetches the current rules/default, prepends the
generated block rules to every bucket's http[] (keeping each bucket's
uuid, auth, egress, and any rules you've already written), and PUTs
the complete replacement in one step:
python3 tools/adblock_to_mitmania.py \
--control "unix://$PWD/mitmania.sock"
The submitted table still goes through the same parser, compiler, coverage
check, and control API size limit as a hand-written PUT — a malformed
generation fails closed; it never applies partially.
3. Verify¶
curl -v https://<a-domain-from-generated-domains.txt>
Expect a clean SSL_ERROR_SYSCALL/connection reset — no certificate,
because connection:{accept:false} never attempts TLS. Confirm the access
log agrees:
"outcome":"denied","verdict":"deny","mitm":"false"
An unrelated site should be unaffected — the trailing mitm:false splice
still allows anything the generated rules didn't match.
4. Keep it fresh¶
Filter lists are maintained upstream and change; re-run the same command periodically (a systemd timer or cron job) to pick up new entries. Re-running is safe to repeat: the converter marks its own output with an internal boundary, so a fresh run replaces the previous generation instead of prepending onto it indefinitely — hand-written rules placed after that boundary are never touched or duplicated.
No exception mechanism yet
Generated rules are always prepended ahead of your existing http[]
rules, so first-match-wins picks a generated block before an exception
written after it ever runs. To allow a hostname a preset would
otherwise block, remove it from the source list you feed the converter
(a local, edited copy of the filter file, or a narrower --preset
selection) rather than relying on rule ordering.
What still works¶
- top-down connection-phase matching on
host,port, andproto— the same first-match ordering as every other rule, so a narroweraccept:trueexception ahead of a broadaccept:falseblock still wins; - transparent interception (REDIRECT/TPROXY): the match runs against the TLS ClientHello's SNI, so this works with zero client configuration and no CA install anywhere on the network — see Intercept without client proxy settings for the listener setup;
- explicit-proxy CONNECT and absolute-form requests, matched on the authority instead of SNI;
- one access-log record per refused connection (
outcome: "denied"), plus metrics (mitmania.requests.total{verdict="deny",mitm="false"}).
What you give up¶
connection: {accept: false} cannot see method, path, headers, or body — it
runs before any of that is visible, the same as mitm:false. It cannot
carry message-phase match fields, a mitm value, or request/response
actions; a rule that needs those (e.g. a custom block page instead of a
silent refusal) needs mitm:true and a request-phase raise action
instead, which does require the client to trust the signing CA.
Warning
A large blocklist compiled from third-party sources is still policy you
are choosing to enforce. Review sources before trusting them, and keep
egress[] deny-first regardless — connection: {accept: false} governs
which hosts get matched, not what an allowed connection is permitted
to reach.
Next: choose REDIRECT or TPROXY, or see
the rule-file schema for the full
connection/mitm field reference.