Gives every browser on the machine ad and tracker blocking, including WebKitGTK ones like surf that have no extension mechanism and so cannot run uBlock Origin. 80,002 rules from the StevenBlack list load in 82ms and sit in 9MB resident. A proxy receives CONNECT doubleclick.net:443 before any TLS handshake, so a blocked host is refused without decrypting anything. Ads and trackers are third-party hosts, which is why host-level refusal captures nearly all of the weight while leaving traffic sealed: no certificate authority in the trust store, no CA private key on disk, and no visibility into a bank session this program merely relays. What that cannot do is cosmetic filtering and first-party ads, which need TLS interception. That is omitted deliberately, and the README records the reasoning and where the seam would be, rather than leaving it looking like an oversight. CONNECT to a blocked host 403, refused before the handshake CONNECT to anything else tunnelled bytes, untouched plain HTTP to a blocked host 204, so a beacon looks empty not failed plain HTTP otherwise forwarded and relayed Matching is by domain suffix, so a rule for doubleclick.net covers stats.g.doubleclick.net; lookups walk the labels of the requested host rather than the list. Hosts files, bare domain lists and the ||domain^ subset of Adblock syntax are accepted, while rules needing response inspection are skipped rather than half-applied. Allow rules win at any depth, and IP addresses are never blocked, since hosts files are full of them as addresses. Lists cache for a week and fall back to a stale copy when a refresh fails. Connections are capped at 96 with a Drop guard releasing the slot even on panic, and only the loopback interface is bound.
4.4 KiB
furst-proxy
A filtering HTTP proxy. Point any browser at it and ad and tracker hosts stop
resolving — including in surf and other WebKitGTK browsers, which have no
extension mechanism and therefore no uBlock Origin.
80,000 rules, ~9MB resident, 82ms to start.
It does not decrypt your traffic
A proxy receives CONNECT doubleclick.net:443 before any TLS handshake
happens. That is enough to refuse the connection, so blocking needs no
certificate authority in your trust store and no visibility into the contents
of anything — your bank session is a sealed tunnel this program relays without
being able to read.
Ads and trackers are third-party hosts, so refusing hosts is most of the win. What it cannot do, by construction:
- hide elements cosmetically (uBlock's
##rules) - block ads served from the site's own domain
- filter inside an HTTPS response
Those need TLS interception. That is a deliberate omission, not a missing feature — see the note below.
Use
furst-proxy --init # write ~/.config/furst/proxy.toml
furst-proxy --update # fetch the blocklists
furst-proxy # run in the foreground
furst-proxy --test doubleclick.net # would this be blocked?
furst-proxy --env # shell exports
furst-proxy --pac ~/.furst.pac # proxy auto-config file
Point a browser at 127.0.0.1:8228 for both HTTP and HTTPS:
eval "$(furst-proxy --env)" # anything honouring http_proxy
surf https://example.com/ # WebKitGTK reads the environment
For Firefox, set the manual proxy in Settings → Network Settings, or load the PAC file. Visit http://furst.proxy/ through the proxy for a status page: rules loaded, requests, share blocked, bytes relayed.
What happens to a request
CONNECT to a blocked host |
403, refused before the handshake |
CONNECT to anything else |
tunnelled bytes, untouched |
| plain HTTP to a blocked host | 204 No Content |
| plain HTTP to anything else | forwarded, relayed |
Blocked plain-HTTP requests get 204 rather than an error, so a blocked script
or beacon looks like an empty answer instead of a failure the page retries.
Configuration
~/.config/furst/proxy.toml:
port = 8228
lists = ["https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts"]
# Webfont and analytics CDNs: a webfont is bytes, a layout pass and a repaint
# for no information, which is a bad trade on an old CPU.
block_heavy = true
block = ["extra.tracker.example"]
allow = ["fonts.gstatic.com"] # wins over everything
Lists are cached under $XDG_CACHE_HOME/furst/lists for a week; --update
refreshes them. If a refresh fails, the stale copy is used — degraded filtering
beats none. Hosts files, bare domain lists, and the ||domain^ subset of
Adblock syntax are all accepted; rules that need response inspection
($third-party, paths, wildcards) are skipped rather than half-applied.
Matching is by domain suffix, so doubleclick.net also covers
stats.g.doubleclick.net. Lookups walk the labels of the requested host, so
cost is the number of dots in it, not the size of the list.
What TLS interception would add
A --mitm mode would generate a local CA, mint a certificate per host, and
decrypt each connection so responses could be filtered. It would buy cosmetic
filtering and first-party ad blocking.
It was left out because the trade is poor here:
- every HTTPS page would be decrypted by this program, so a bug in it becomes a bug in the confidentiality of everything you browse
- a CA private key on disk is a credential worth stealing
- certificate minting and validation is where proxies get subtly wrong, and getting it wrong silently downgrades security rather than breaking loudly
furst-servealready renders most pages without a browser engine, so the remaining first-party ads are seen rarely
If you want it anyway, the seam is handle() in proxy.rs: after accepting
CONNECT for an unblocked host, terminate TLS with a minted certificate
instead of tunnelling.
Limits
- Only the loopback interface is bound. This is a personal proxy, not a network service; do not expose it.
- Plain HTTP is relayed with
Connection: close, so no keep-alive on that path. HTTPS tunnels are unaffected. - At most 96 concurrent connections, so a runaway page cannot exhaust a 4GB
machine. Beyond that the proxy answers
503.