IP and DNS blocking for pfSense (CE and Plus).
pfBlockerNG downloads curated IP and domain feeds and turns them into live firewall and DNS policy: IP feeds become pf alias tables with automatic firewall rules, GeoIP lets you block or permit by country/continent, and DNSBL enforces domain blocklists directly in the Unbound resolver. It adds reports, alerts, a dashboard widget, and HA/CARP sync on top.
This repository is a community-maintained fork — pfBlockerNG/pfBlockerNG — of the original package by BBcan177. It is licensed under the Apache License 2.0.
For day-to-day usage and configuration, the upstream Netgate pfBlockerNG documentation applies. This README covers what is specific to installing this fork and the features it adds on top; the per-feature design records live under
.ADRs/.
- IP blocking — IPv4/IPv6 feeds → pf alias tables + automatic firewall rules (Deny / Permit / Match / Native actions, inbound/outbound/both), with dedup, CIDR aggregation, and suppression of your own networks.
- GeoIP — block or permit by continent or country (MaxMind GeoLite2).
- DNSBL — domain blocklists enforced inside Unbound by a Python matcher; sinkhole-VIP or NULL responses, SafeSearch enforcement, and per-name reports.
- Adblock Plus / EasyList feeds — full ABP syntax parsed in Python: allow
(
@@) exceptions, regex rules, and$important/$badfilterprecedence (ADR-07). - IDN homoglyph protection — a TR39 mixed-script analyzer blocks deceptive
cross-script look-alike domains (e.g. a Cyrillic
аpple) (ADR-08). - Zero-downtime DNSBL updates — feed/data updates swap the blocklist without restarting Unbound; queries keep flowing (ADR-10).
- Aggregated ("Uber") aliases — opt-in Native aliases
(
pfB_<Type>_Aggregated_v4/_v6) holding the combined, CIDR-aggregated set of a whole action type, for reference by your own rules or an external service such as HAProxy (ADR-11). - Update Hooks — run your own
pre/postshell commands on each update pass (e.g. a graceful HAProxy reload), with a documented environment contract (ADR-12). - Automatic DNSBL sinkhole VIP — pfBlockerNG can own the sinkhole Virtual IP for you instead of a manual setup (ADR-13).
Two channels track two branches of this repository:
| Channel | Branch | Package | For |
|---|---|---|---|
| Stable | main |
pfSense-pkg-pfBlockerNG |
Production use |
| Development | devel |
pfSense-pkg-pfBlockerNG-devel |
Latest features, early testing |
| Nightly | devel (HEAD, built nightly) |
pfSense-pkg-pfBlockerNG-nightly |
Bleeding edge; self-hosted repo only |
New work lands on devel first; once it has settled it is promoted to main to
cut a stable release. Nightly rebuilds the devel tip each night as a separate,
opt-in package available only from this fork's self-hosted repository (Option 2).
The three packages are mutually exclusive — install one. Choose stable unless
you specifically want to track development builds.
pfBlockerNG ships in pfSense's built-in package catalog. In the webConfigurator go
to System ▸ Package Manager ▸ Available Packages, search for pfBlockerNG, and
install pfBlockerNG (stable) or pfBlockerNG-devel (development). This is the
simplest path and the right one for most users.
To run this fork's latest builds — ahead of, and independent of, the Netgate
catalog — add our self-hosted FreeBSD pkg repository
(ADR-17,
ADR-20). It resolves dependencies
normally (no pkg add -f). Run the bootstrap on the firewall over SSH, then install:
./scripts/add-repo.sh devel # or: stable
pkg install pfSense-pkg-pfBlockerNG-develadd-repo.sh writes /usr/local/etc/pkg/repos/pfblockerng-<channel>.conf, runs
pkg update, and verifies the package is visible. No variant argument is needed — the
script auto-detects CE vs Plus via the routing layer. The configuration it writes is:
pfblockerng-devel: {
url: "https://pkg.pfblockerng.workers.dev/${ABI}",
mirror_type: none,
signature_type: none,
priority: 100,
enabled: yes
}${ABI}is apkg(8)variable (expanded bypkg, not the shell), so one configuration follows the box across a pfSense OS upgrade.- The routing URL (
pkg.pfblockerng.workers.dev) is a Cloudflare Worker that reads the pfSenseUser-Agenton each request and redirects to the correct variant catalog: CE boxes getce-2.8/${ABI}/(php83 dep); Plus boxes getplus-26.03/${ABI}/(php85 dep). The conf is written once and never needs re-running on a pfSense upgrade — the Worker reroutes automatically. - The repository is NONE-signed — trust is anchored in HTTPS to the host.
priority: 100places it above the NetgatepfSenserepository, so cross-repo resolution (and the webConfigurator's Install button) picks our build.
Transition note (ADR-20): If you configured the repo before 2026-06-10, re-run
sh scripts/add-repo.sh devel(orstable) to refresh the conf to the Worker URL. The legacypfblockerng.github.io/pkg/${ABI}/path continues to serve CE packages during the transition window. Installs and updates work via the Install button orpkg upgrade, but pfSense's GUI won't show an "update available" badge for our builds.
To track the devel tip rebuilt every night, opt into the separate nightly
channel — a distinct package pfSense-pkg-pfBlockerNG-nightly served from a
nightly/ catalog subtree:
./scripts/add-repo.sh nightly
pkg install pfSense-pkg-pfBlockerNG-nightlyIt conflicts with the stable and -devel packages (they install the same files),
so it replaces whichever you had; switch back any time with ./scripts/add-repo.sh devel
(or stable) and pkg install the release package. Nightly versions order as
<target>.YYYYMMDD.N, so pkg upgrade always moves to the newest build, and the
source commit rides the package — pkg info -A pfSense-pkg-pfBlockerNG-nightly shows it.
The last 14 builds are kept, so you can roll back by installing an older version
explicitly (pkg install pfSense-pkg-pfBlockerNG-nightly-<version>).
pkg upgrade pfSense-pkg-pfBlockerNG-devel # or the stable package nameOn a FreeBSD machine with the ports tree available, the package can be built
directly — make package in net/pfSense-pkg-pfBlockerNG (stable) or
net/pfSense-pkg-pfBlockerNG-devel (devel); the resulting .pkg lands in
work/pkg/.
Most configuration lives in the webConfigurator under Firewall ▸ pfBlockerNG; the Netgate documentation is the general reference. A couple of this fork's additions are worth calling out.
The Update Hooks tab runs your own shell commands at the start (pre) and end
(post) of every update pass — for example to reload a downstream service when the
blocklist changes. Each enabled hook runs as root via /bin/sh -c (the same trust
class as pfSense's shellcmd/cron) under a timeout; a hook's failure is logged and
never aborts the update, and with no enabled hooks the pass is unchanged.
A post hook receives this environment:
| Variable | Value |
|---|---|
PFB_WHEN |
pre or post |
PFB_TRIGGER |
cron | update | force-reload |
PFB_IP_CHANGED |
1 if a firewall rule changed this pass — a content-only alias refresh leaves it 0 |
PFB_DNSBL_CHANGED |
1 if DNSBL data changed this pass |
PFB_CHANGED_IP_ALIASES |
space-separated IP aliases (pfB_*) whose contents changed (empty when none) |
PFB_CHANGED_DNSBL_GROUPS |
space-separated DNSBL groups (DNSBL_*) updated (empty when none) |
PFB_STATUS |
reserved — currently always ok |
To act when the blocklist data changed, guard on a non-empty
PFB_CHANGED_IP_ALIASES, notPFB_IP_CHANGED=1— the latter fires only on a rule change and misses content-only feed refreshes.
Reload HAProxy after an IP update — the motivating use case: block a
Cloudflare-fronted real client IP via an aggregate alias, refreshed by a graceful
HAProxy reload. Add a post hook with:
[ "$PFB_IP_CHANGED" = "1" ] && echo 'require_once("haproxy/haproxy.inc"); haproxy_check_run(1);' | /usr/local/sbin/pfSsh.phpNotify a webhook of what changed — fires on any blocklist-data change (including
content-only refreshes). Each field rides its own --data-urlencode so the
space-separated lists are encoded correctly:
[ -n "$PFB_CHANGED_IP_ALIASES" ] && /usr/local/bin/curl -sS -m 5 \
--data-urlencode "ip_aliases=$PFB_CHANGED_IP_ALIASES" \
--data-urlencode "dnsbl_groups=$PFB_CHANGED_DNSBL_GROUPS" \
https://example.invalid/pfblockerng-updateThe full trust model, the complete HAProxy frontend ACL setup, and the URL-encoding rules are in ADR-12 and CONTRIBUTING.md.
- Using pfBlockerNG: Netgate documentation.
- This fork's design decisions (one record per feature/subsystem):
.ADRs/. - Developing, testing, and releasing this package: CONTRIBUTING.md.
Development setup, the test suites (unit, PHP, shell, and the live-VM smoke / Web UI tiers), linting, the build and release pipelines, and the internals of each subsystem are all documented in CONTRIBUTING.md.
Licensed under the Apache License, Version 2.0.
- Original author: BBcan177.
- Copyright © 2015–2026 Rubicon Communications, LLC (Netgate) and contributors.
- GeoIP data by MaxMind Inc. (GeoLite2).