Add a free attacker blocklist to pfSense (pfBlockerNG), OPNsense and MikroTik

Add a free list of confirmed attacker IPs to pfBlockerNG, an OPNsense URL table alias or a MikroTik address list, with tested settings and a safe refresh.

To feed pfBlockerNG a free attacker blocklist, add an IPv4 list with the source https://ffraud.com/free/badips.txt, the format Auto and the action Deny Inbound. Set it to update every 6 hours. OPNsense loads the same address as a URL Table (IPs) alias, blocked by one rule on WAN. On MikroTik, a short script fills an address list, and one raw rule drops it.

The list holds 2,000 IPs confirmed abusive six or more times in the last 14 days. FFraud rebuilds it every six hours and asks for no account or key.

#Which free list should a firewall load?

FFraud publishes four plain-text lists, one IP per line, with the strongest entries first. Each file states its rule in a comment at the top.

ListWhat earns a listingIPs
badips.txtConfirmed abusive 6 or more times in 14 days2,000
worst.txtConfirmed abusive 10 or more times in 14 days500
webattack.txtSeen attacking web apps, confirmed 4 or more times2,000
freships.txtFirst seen in the last 7 days, confirmed 3 or more times2,500

Start with badips.txt on a firewall that guards a whole network. worst.txt is the strictest of the four. webattack.txt suits a network that runs web servers. freships.txt catches new attackers on fewer confirmations.

On October 25, 2026, 184 of the 2,000 IPs in badips.txt were Tor exits on the Tor Project's own list. Loading it also blocks new connections from those exits.

#How do you add the list to pfBlockerNG?

pfBlockerNG puts each feed into an alias and writes the block rule for you (Netgate documentation). The steps follow the forms of pfBlockerNG 3.2.15, the version in pfSense's package source on October 25, 2026.

  1. Under Firewall > pfBlockerNG > General, tick Enable. On the IP tab, select WAN under Inbound Firewall Rules.
  2. Open IP > IPv4 and add a list. Name it FFraud_badips, because the name takes no spaces or special characters.
  3. In the source row, choose the format Auto and the state ON. Paste https://ffraud.com/free/badips.txt as the Source and FFraud_badips as the Header/Label.
  4. Set Action to Deny Inbound and Update Frequency to Every 6 hours, then save.
  5. On the Update tab, choose Reload and IP, then run it to load the list now.

The update frequency must fit the global CRON settings on the General tab, which run every hour at minute 00 by default. The block rule goes ahead of your other rules, unless you change pfBlockerNG's rule order.

How pfBlockerNG reads each line

pfBlockerNG skips every line that starts with #, so the comment header never reaches the firewall. A feed whose address ends in .txt goes through its Auto parser, one IP per line. That parser has one rule to know: a bare IP whose last number is 0 becomes a whole /24 network.

We ran all four lists through a copy of that parser on October 25. badips.txt and worst.txt loaded as single IPs. webattack.txt held 1 IP that ends in .0 and freships.txt held 15, and pfBlockerNG reads each of them as 256 addresses. An IPv4 list also skips IPv6 lines, so the 27 IPv6 entries of freships.txt need an IPv6 list of their own.

#How do you add the list to OPNsense?

  1. Go to Firewall > Aliases, add an alias named ffraud_badips and choose the type URL Table (IPs).
  2. Enter https://ffraud.com/free/badips.txt as the content and set Refresh Frequency to 0 days and 6 hours. Save and apply.
  3. Under Firewall > Rules, on the WAN interface, add a rule with the action Block, the IP version IPv4 and the source ffraud_badips.
  4. Move the rule above every WAN rule that passes traffic. OPNsense rules are first match by default, so a pass rule above it wins.
  5. Check what loaded under Firewall > Diagnostics > Aliases.

OPNsense's own guide for the Spamhaus DROP list builds the same alias and WAN rule. Its manual asks for one IPv4 or IPv6 address per line and ignores lines that start with #, which is how FFraud writes its lists.

One trap is worth knowing. OPNsense treats any line that is not an address as a host name and tries to resolve it. Our test found no such line in the four lists. For the IPv6 entries of freships.txt, add a second rule with the IP version IPv6.

#How do you load the list on MikroTik?

RouterOS fills an address list in three ways: you add entries, firewall rules add them, or the router resolves a DNS name (RouterOS manual). None of them reads a list from a URL. The recipe uses a script that downloads the list with /tool fetch and adds each IP. A raw rule then drops the list before connection tracking.

The script is written for RouterOS 7.19 or later, because check-certificate=yes uses the root certificates that 7.19 added. Add the drop rule once:

RouterOS terminal
/ip/firewall/raw/add chain=prerouting action=drop in-interface-list=WAN \
    src-address-list=ffraud-a comment="ffraud blocklist"

Then add the script. It waits for the default route, loads the list that the rule does not use, points the rule at it and empties the old one:

RouterOS terminal: the script
/system/script/add name=ffraud-blocklist source={
:local ffUrl "https://ffraud.com/free/badips.txt"
:local ffTries 0
:while (([:len [/ip/route/find where dst-address=0.0.0.0/0 active]] = 0) && ($ffTries < 60)) do={
  :delay 5s
  :set ffTries ($ffTries + 1)
}
:local ffRule [/ip/firewall/raw/find where comment="ffraud blocklist"]
:if ([:len $ffRule] != 1) do={ :error "ffraud: add the raw rule first" }
:local ffOld [/ip/firewall/raw/get $ffRule src-address-list]
:local ffNew "ffraud-a"
:if ($ffOld = "ffraud-a") do={ :set ffNew "ffraud-b" }
:local ffData ""
:do {
  :set ffData ([/tool/fetch url=$ffUrl check-certificate=yes output=user as-value]->"data")
} on-error={
  :log warning "ffraud: download failed, list left as it is"
  :error "ffraud: download failed"
}
:local ffSize [:len $ffData]
:if ($ffSize >= 64512) do={
  :log warning "ffraud: download reached the fetch limit, list left as it is"
  :error "ffraud: download cut"
}
/ip/firewall/address-list/remove [find where list=$ffNew]
:local ffCount 0
:local ffStart 0
:local ffEnd 0
:while ($ffStart < $ffSize) do={
  :set ffEnd [:find $ffData "\n" $ffStart]
  :if ([:len $ffEnd] = 0) do={ :set ffEnd $ffSize }
  :local ffLine [:pick $ffData $ffStart $ffEnd]
  :set ffStart ($ffEnd + 1)
  :if ([:typeof [:toip $ffLine]] = "ip") do={
    :do {
      /ip/firewall/address-list/add list=$ffNew address=$ffLine timeout=1w
      :set ffCount ($ffCount + 1)
    } on-error={}
  }
}
:if ($ffCount < 100) do={
  /ip/firewall/address-list/remove [find where list=$ffNew]
  :log warning "ffraud: only $ffCount addresses in the download, list left as it is"
  :error "ffraud: short list"
}
/ip/firewall/raw/set $ffRule src-address-list=$ffNew
/ip/firewall/address-list/remove [find where list=$ffOld]
:log info "ffraud: $ffCount addresses loaded into $ffNew"
}

Run it at boot and every 6 hours. A startup entry with an interval does not run at boot itself, so it takes two entries (RouterOS manual):

RouterOS terminal: the schedule
/system/scheduler/add name=ffraud-blocklist-boot start-time=startup on-event=ffraud-blocklist
/system/scheduler/add name=ffraud-blocklist-6h interval=6h on-event=ffraud-blocklist
  • Two lists. The rule switches only once the new list is full, so traffic always meets a whole list, never half of one.
  • The size limit. With output=user, /tool fetch keeps at most 64,512 bytes and cuts a longer body without an error. The script refuses a download that reaches the limit.
  • Every line is checked. RouterOS stores text that is not an address as a DNS name, without an error. The script adds only lines that :toip reads as an IPv4 address, so comments and IPv6 lines stay out.
  • Reboots. Entries with a timeout live in memory, and a reboot clears them. The boot entry loads the list again once the default route is up.
  • Names. Each variable starts with ff. A variable named like a property, such as list, is misread inside a find (RouterOS manual).
FFraud's free lists against the RouterOS fetch limitBytes per list on October 25, 2026 (build of 18:17 UTC), beside the 64,512 bytes that /tool fetch can hand a script
  1. worst.txt7,361 bytes
  2. badips.txt28,752 bytes
  3. webattack.txt29,362 bytes
  4. freships.txt36,058 bytes
  5. RouterOS limit64,512 bytes

Source: FFraud free lists, read October 25, 2026; RouterOS manual, Fetch

We wrote the script from the RouterOS 7.26 manual and checked the downloads against every limit it enforces. It is untested on a real router, so run it once with /system/script/run ffraud-blocklist. Then read the log and the list before you trust the schedule. The scheduler is not available when the device mode is home.

#What happens when the list cannot be downloaded?

All three setups keep the last good list when a download fails, and none of them blocks all traffic. That suits a blocklist, which is one layer of defense: a feed outage should never cut a network off.

SetupWhen a download failsWhere it says so
pfBlockerNGLogs Download FAIL and restores the last downloaded filepfblockerng.inc
OPNsenseLogs the error and rolls back to the last loaded IPsAlias code
MikroTik scriptLogs a warning and stops before it touches a listThe script above

A first download that fails leaves the list empty on all three, so traffic passes until a download works. After a reboot, the MikroTik lists start empty for the same reason. Each setup also gives up on a slow server. pfBlockerNG waits 15 seconds for a connection, OPNsense allows 120 seconds, and RouterOS stops after 10 seconds without data.

#How often should the firewall refresh the list?

FFraud rebuilds the four lists every six hours, so a refresh every 6 hours picks up each new build. A shorter interval downloads the same file again. The lists are served with Cache-Control: public, max-age=1800, so a proxy between you and FFraud may hand out a copy up to 30 minutes old.

#How do you check that it works?

Count the loaded IPs first. pfBlockerNG names its table pfB_ plus the list name plus _v4, and OPNsense names the table after the alias:

pfSense or OPNsense shell
# pfSense with pfBlockerNG
pfctl -t pfB_FFraud_badips_v4 -T show | wc -l
# OPNsense
pfctl -t ffraud_badips -T show | wc -l
RouterOS terminal
/ip/firewall/address-list/print count-only where list~"^ffraud-"

Then ask the free API about one listed IP. It needs no key and says why the address is on the list:

curl https://api.ffraud.com/public/ip/157.66.26.151

Our test fed the four lists to copies of the pfBlockerNG and OPNsense line parsers and to the script's line check. The fields are explained in the IP intelligence docs.

test run, October 25, 2026 (lists built 18:17 UTC)
list            IPs   pfBlockerNG  as /24  IPv6 skipped  OPNsense  MikroTik script  bytes
badips.txt     2000         2000       0             0      2000             2000  28752
webattack.txt  2000         2000       1             0      2000             2000  29362
worst.txt       500          500       0             0       500              500   7361
freships.txt   2500         2473      15            27      2500             2473  36058
every file: line feeds only, under the 64512-byte fetch limit,
HTTP 200 for the user agents of pfBlockerNG, OPNsense and RouterOS

#Can you load the full 1.6 million list instead?

FFraud's malicious IP database lists every confirmed attacker, 1,597,114 IPs in the build of 21:30 UTC on October 25. pfBlockerNG and OPNsense can both read the file from GitHub, but it takes memory.

pfSense allows 400,000 table entries by default. Netgate asks for at least double the entries of all aliases, because the firewall loads a new list beside the old one. It counts about 1 KB of memory per entry to be safe. OPNsense starts at 1,000,000 entries and raises its default with memory.

In pfBlockerNG, the 3,670 rows that end in .0 would each block a /24. A MikroTik script cannot fetch the file at all, since it is far above the 64,512-byte limit. For most networks, a capped list of 2,000 is the better trade. The open data docs list every file.

#Questions people ask

Which IP blocklists should I use in pfBlockerNG?

Start with one list of recent attackers, such as badips.txt. OPNsense's own guide adds Spamhaus DROP, a list of networks hijacked or leased by spam and crime operations. Each extra list raises the chance of a false positive, so add them one at a time. Our comparison of free blocklists counts eight of them.

Why does pfBlockerNG say it cannot allocate memory when it loads lists?

One known cause is the table limit. When Firewall Maximum Table Entries is too small for all aliases, Netgate warns that the firewall may fail to load the ruleset. Raise it under System > Advanced > Firewall & NAT to at least double the entries of all aliases.

How do I import an IP list from a file into a MikroTik address list?

For a one-time load, a file of add commands and /import works, but the import stops at the first IP already in the list (RouterOS manual). For a list that changes, use a script that fetches the file and adds each line, as above.

Is there a size limit when MikroTik fetches a list into a variable?

Yes. With output=user, /tool fetch keeps at most 64,512 bytes and cuts the rest without an error. Compare the length of the data with that limit to catch a cut list.

How do I use a URL table alias as a blocklist in OPNsense?

Create a URL Table (IPs) alias with the list's address and a refresh frequency. Then use it as the source of a block rule on WAN. Firewall > Diagnostics > Aliases shows what it loaded.

Should I paste IPs into the pfBlockerNG custom list or use a feed URL?

Use a feed URL for any list that changes. A custom list lives inside the firewall's config.xml file and changes only when you edit it.

To see why one IP landed on the list, paste it into the free IP blacklist check. For a Linux server, our ipset guide loads the same lists into the kernel.

Sources

  1. pfBlocker-NG Package, Netgate, pfSense documentation, August 29, 2025
  2. Firewall & NAT: Firewall Maximum Table Entries, Netgate, pfSense documentation, April 24, 2026
  3. pfSense-pkg-pfBlockerNG 3.2.15: pfblockerng.inc and the settings forms, pfSense project, FreeBSD-ports on GitHub
  4. Aliases: URL Tables, OPNsense documentation
  5. Configure Spamhaus DROP, OPNsense documentation
  6. Rules: processing order, OPNsense documentation
  7. Alias parsing and loading (lib/alias, update_tables.py, filter.inc), OPNsense core on GitHub
  8. Fetch, MikroTik, RouterOS manual 7.26
  9. Address lists, MikroTik, RouterOS manual 7.26
  10. Scheduler, MikroTik, RouterOS manual 7.26
  11. Scripting, MikroTik, RouterOS manual 7.26
  12. What's new in 7.19, MikroTik, RouterOS changelog, May 22, 2025
  13. Tor bulk exit list, The Tor Project, October 25, 2026
  14. Free lists badips, webattack, worst and freships, build of 18:17 UTC, FFraud, October 25, 2026