- Home
- Integrations
- Pi-hole
Setup guide
Pi-hole malware blocklist as an authenticated adlist
No credit card required · Free API key
On this page
On this page08
What you get
Pi-hole blocks names, not addresses, so only the domain lists apply. Load one critical list, in plain or adblock syntax, and add the category lists if you want them.
| List | Entries | Rebuilt | Plans |
|---|---|---|---|
| blocklist-domains-critical.txtDefault. Domains listed by 6 or more threat sources, or 3 or more with a critical category. Exact-name blocking. | about 2 million | every 12 h | Basic, Pro, and Enterprise |
blocklist-domains-critical-adguard.txtThe same domains as ||domain^ rules, which also block subdomains. Core v5.16.1 and FTL v5.22 or later. Load this one or the plain list, not both. | about 2 million | every 12 h | Basic, Pro, and Enterprise |
| blocklist-domains-c2.txtCommand-and-control domains reported by C2 trackers, whatever their level. | about 22,000 | every 12 h | Basic, Pro, and Enterprise |
| blocklist-domains-ransomware.txtDomains in the ransomware category, whatever their level. | about 3,600 | every 12 h | Basic, Pro, and Enterprise |
Entries rounded from the build of ; each list is rebuilt every 12 hours. Today’s counts are public and need no key.
What each plan receives
- FreeFree account, or no key: the first 10% of each list, marked
X-Blocklist-Version: lite. - Basic, Pro, and EnterpriseBasic, Pro, and Enterprise: every list in full.
- Pro and EnterpriseTAXII 2.1 collections, for platforms that read STIX indicators: Pro and Enterprise.
GET https:/
Full list or 10% sample
| Field | Basic, Pro, and Enterprise | Free |
|---|---|---|
X-Blocklist-Version: | full | lite |
X-Blocklist-Percentage: | 100 | 10 |
Total entries: | <COUNT> | <COUNT> |
First lines of the file
# IsMalicious.com Blocklist - Domains (Critical)
# Format: Plain
# Generated: <BUILD_TIME>
# Total entries: <COUNT>
# Update frequency: every 12 hours
# Category: All
# Threat level: Critical
# Website: https://ismalicious.com
# © <YEAR> IsMalicious (compilation). Licensed to the downloading account under https://ismalicious.com/terms; redistribution of the compilation prohibited. Third-party entries remain under their providers' licences — see https://ismalicious.com/sources.
#Values in angle brackets are set by each build.
Prerequisites
- Pi-hole Core v5.2.3 or later: earlier releases reject an
@in a list address. Pi-hole v6 is the current line. - A shell on the Pi-hole host, or on the Docker host, to schedule gravity.
- An isMalicious API key and secret, from Account › API access.
- Outbound HTTPS (TCP 443) over IPv4 from Pi-hole to
api.ismalicious.com, which has no IPv6 address.
Set it up
Copy your API key and secret
Open Account › API access and copy the API Key and the API Secret. The full list needs a Basic, Pro, or Enterprise plan; a Free key loads the 10% sample.Build the list address
Put the key, a colon and the secret before the host, followed by@. Keys and secrets are UUIDs, so they need no encoding. Useapi.ismalicious.com: the ismalicious.com edge refuses some sources with a 403.List addressURL https://<API_KEY>:<API_SECRET>@api.ismalicious.com/blocklist/download/blocklist-domains-critical.txtList address, subdomains includedURL https://<API_KEY>:<API_SECRET>@api.ismalicious.com/blocklist/download/blocklist-domains-critical-adguard.txtAdd the list to Pi-hole
Pi-hole v6: Group Management › Lists, paste the address in Address, enter a Comment such asisMalicious critical, keep the Default group and click Add blocklist. Pi-hole v5: Group Management › Adlists, then Address, Comment and Add. On v6 the API does the same: the block below verifies FTL’s own certificate, reads the password from the terminal and the address from a file, so neither lands in a process listing or the shell history, and closes the session at the end.Pi-hole v6 APIsh # FTL's own CA, copied from /etc/pihole/tls_ca.crt on the Pi-hole host. Add # --resolve pi.hole:443:<PIHOLE_IP> when pi.hole does not resolve. CA=./pihole-tls_ca.crt API=https://pi.hole/api # The password is read from the terminal: it stays out of ps and history. # jq escapes the quotes and backslashes it may hold. printf 'Pi-hole password: ' >&2; stty -echo; IFS= read -r PW; stty echo; echo >&2 SID=$(printf '%s' "$PW" | jq -Rs '{password: .}' | curl -sS --cacert "$CA" -X POST "$API/auth" --data @- | jq -r .session.sid) # list.json (mode 600), written with an editor, holds the address with the key: # {"address":"https://<API_KEY>:<API_SECRET>@api.ismalicious.com/blocklist/download/blocklist-domains-critical.txt","comment":"isMalicious critical","groups":[0]} curl -sS --cacert "$CA" -X POST "$API/lists?type=block" -H "X-FTL-SID: $SID" \ -H "Content-Type: application/json" --data @list.json curl -sS --cacert "$CA" -X POST "$API/action/gravity" -H "X-FTL-SID: $SID" curl -sS --cacert "$CA" -X DELETE "$API/auth" -H "X-FTL-SID: $SID"Run gravity
Tools › Update Gravity › Update, orsudo pihole -gon the host. Gravity downloads the list and writes it togravity.db.Run gravity every 12 hours
Pi-hole updates its lists once a week, at a random minute in the night. Create a cron file of your own with theupdateGravityline of/etc/cron.d/piholeand a 12-hour schedule outside hours 3 and 4, where the weekly run falls: gravity has no lock. Do not edit/etc/cron.d/pihole: updates overwrite it. The line runs aspiholeon v6 and asrooton v5. On Docker, schedulepihole -gthroughdocker execon the host./ etc/ cron. d/ ismalicious-gravitycron # Pi-hole v6 (user pihole; on v5, write root). The lists are rebuilt every 12 hours. 17 5,17 * * * pihole PATH="$PATH:/usr/sbin:/usr/local/bin/" pihole updateGravity >/var/log/pihole/pihole_updateGravity.log || cat /var/log/pihole/pihole_updateGravity.logKeep the key out of Pi-hole (optional)
- Pi-hole stores the address in clear in
gravity.db, shows it on the Lists page and prints it in every gravity log. - To avoid that, create the root-only netrc file below, fetch the list with the script, which refuses an error, a body that is not a list, the 10% sample and a file whose entry count differs from its header, and add
file:/as the address./ / var/ lib/ ismalicious/ blocklist-domains-critical. txt - The script makes the copy readable for gravity.
- On Docker, mount
/var/lib/ismaliciousinto the container read-only and keep the samefile://address.
Create the file, root-onlysh install -d -m 700 /etc/ismalicious [ -e /etc/ismalicious/netrc ] || install -m 600 /dev/null /etc/ismalicious/netrc chmod 600 /etc/ismalicious/netrc # Then write the three lines below into it with an editor, unless it holds them already./ etc/ ismalicious/ netrcnetrc machine api.ismalicious.com login <API_KEY> password <API_SECRET>/ usr/ local/ sbin/ ismalicious-fetch. shsh #!/bin/sh # Runs as root, before gravity. /etc/ismalicious/netrc (mode 600) holds the key. # The copy is made readable for gravity, which reads it as pihole (Core # v6.4.2+) or as root; on Docker there is no pihole user on the host. set -eu NETRC=/etc/ismalicious/netrc DIR=/var/lib/ismalicious FILE=blocklist-domains-critical.txt # fetch_list LIST OUT: download one list to OUT and check it. # On any failure OUT is removed and the function returns 1. fetch_list() { if ! code=$(curl --silent --show-error --fail --netrc-file "$NETRC" \ --proto '=https' --max-time 300 --retry 2 \ --output "$2" --write-out '%{http_code}' "https://api.ismalicious.com/blocklist/download/$1"); then rm -f "$2"; echo "$1: download failed" >&2; return 1 fi if [ "$code" != 200 ]; then rm -f "$2"; echo "$1: HTTP $code" >&2; return 1 fi if ! head -n 1 "$2" | grep -q '^[#!] IsMalicious.com Blocklist'; then rm -f "$2"; echo "$1: not an isMalicious list" >&2; return 1 fi if grep -q 'Lite Version' "$2"; then rm -f "$2"; echo "$1: lite list received, check the API key and the plan" >&2 return 1 fi # Every entry ends with a newline, and one header counts them: refuse a # cut, doubled or empty file. if [ -n "$(tail -c 1 "$2")" ]; then rm -f "$2"; echo "$1: cut short, no final newline" >&2; return 1 fi if [ "$(grep -c '^[#!] Total entries:' "$2")" != 1 ]; then rm -f "$2"; echo "$1: not one Total entries line" >&2; return 1 fi total=$(sed -n 's/^[#!] Total entries: \([0-9,]*\)$/\1/p' "$2" | tr -d ,) got=$(grep -c '^[^#!]' "$2" || true) if [ -z "$total" ] || [ "$total" = 0 ] || [ "$got" != "$total" ]; then rm -f "$2"; echo "$1: $got entries, the header says ${total:-none}" >&2 return 1 fi } mkdir -p "$DIR" tmp=$(mktemp "$DIR/.$FILE.XXXXXX") trap 'rm -f "$tmp"' EXIT fetch_list "$FILE" "$tmp" chmod 644 "$tmp" mv -f "$tmp" "$DIR/$FILE" # Schedule it before gravity, in /etc/cron.d/ismalicious-gravity: # 7 5,17 * * * root /usr/local/sbin/ismalicious-fetch.sh # List address in Pi-hole: file:///var/lib/ismalicious/blocklist-domains-critical.txt- Pi-hole stores the address in clear in
Verify it works
- Gravity prints
Status: Retrieval successfulfor the list, thenParsed(from Core v5.17; older releases printN exact domains and M ABP-style domains Imported N domains). - N for a plain list, or M for the adblock one, matches the list’s
countin /blocklist/stats, give or take one rebuild. About a tenth of it is the 10% sample. - Group Management › Lists shows the list as downloaded or unchanged, not as failed.
- On Core v6.1 or later, the first lines of the cached copy in
/etc/pihole/listsCache/(mode 0640: read it with sudo) show# Total entries:with noLite Versionmention. Afile://list is cached aslist.N.local.domains. pihole -qon a domain from the list names the lists that contain it.
# Gravity's summary for the list (Core v5.17+ prints Parsed, older Imported):
sudo pihole -g | grep -E 'Retrieval|Parsed|Imported'
# Core v6.1+ keeps the raw file (mode 0640): a full list has no "Lite Version".
sudo head -12 /etc/pihole/listsCache/list.*.api.ismalicious.com.domains
# The same check without Pi-hole, with the netrc file of the local copy:
sudo curl -sS --netrc-file /etc/ismalicious/netrc -D - -o /dev/null \
"https://api.ismalicious.com/blocklist/download/blocklist-domains-critical.txt" | grep -i '^x-blocklist-version'Troubleshooting
Gravity reports a 401
The key or the secret is wrong or incomplete. Core v6.4.2 and later print The ; older releases print the URL, credentials included. Gravity then keeps its previous copy, so the old list stays active: check the list status after every key rotation. The response body reads “Blocklist not found or empty”; trust the status code. Copy both values again from Account › API access.
Only about 10% of the domains load
The address carries no credentials, or the plan is Free or lapsed: past due, unpaid, canceled, incomplete or paused counts as Free. Check the header of the cached copy and your plan.
The local copy script prints “entries, the header says”, “cut short, no final newline” or “not one Total entries line”
The file was empty, cut short or not one of the lists, as a proxy or captive portal answers. The script keeps the previous copy and exits 1, and gravity keeps reading it.
Gravity says “Invalid Target”
The address holds a character Pi-hole refuses besides the one @, often a placeholder bracket left in or a space. Before Core v5.2.3, any @ is refused: upgrade.
502, 504 or a timeout
A download that takes longer than 30 seconds on our side ends in a timeout or a 502: use a tier or category list from the table.
503 Service Unavailable
The list is being built for the first time, and the response carries Retry-After: 600. Gravity does not retry: it keeps its cached copy until the next run.
404 Not Found
The filename is wrong. Copy it from the table above.
Gravity ignores most of the entries
The header lines are not the cause: Pi-hole skips # and ! lines. When the ignored count is close to the entry count, the wrong file is loaded: a -dnsmasq.txt list, a URL list, or an -adguard.txt list on Pi-hole older than Core v5.16.1 and FTL v5.22. An IP list is skipped silently from Core v6.1 (0 parsed, 0 ignored), and imported before as names nothing matches.
Certificate errors
Gravity’s curl checks the system CA store. Core v6.4.2 and later print curl’s message; older releases report “Connection Refused”. Update ca-certificates, check the system clock, and exempt api.ismalicious.com from TLS inspection.
Limits
- Pi-hole keeps the address, credentials included, in
gravity.db(left mode 664) and up to 10 backups of it under/etc/pihole/gravity_backups/, on the Lists page, inpihole -qoutput and in every gravity log. Thefile://relay of the last step avoids that: rotate the key after switching to it. - A plain list blocks the listed names only. The adblock variant also blocks every subdomain, which reaches more names than the list holds.
- Pi-hole cannot block IP addresses: from Core v6.1 it skips IP lines silently, and before it imported them as names nothing matches, so an IP list blocks nothing. Load
blocklist-ips-*lists on a firewall. - No RPZ, no CIDR and no wildcard entries such as
*.exampleare served; Pi-hole needs none of them. - The 10% sample is the first tenth of an unsorted file, not the riskiest tenth.
- Without your own cron file, Pi-hole runs gravity once a week and can be up to 7 days behind the lists.
Questions
Can Pi-hole send an API key for a blocklist?
Yes. Put the key and the secret before the host in the list address, separated by a colon and followed by @. Pi-hole hands the address to curl, which sends them as HTTP Basic. It needs Core v5.2.3 or later. Pi-hole stores and prints that address in clear; fetching the list with a script and loading it as a file:// address keeps the key on the host.
Does the list block subdomains?
The plain list blocks exact names only. Load blocklist-domains-critical-adguard. instead: Pi-hole reads its ||domain^ rules as the domain and all its subdomains, from Core v5.16.1 and FTL v5.22.
Can Pi-hole block malicious IP addresses?
No. Pi-hole answers DNS queries, so it blocks names only: from Core v6.1 it skips IP lines silently. Load the IP lists on a firewall instead.
How often should gravity run?
Every 12 hours. The lists are rebuilt every 12 hours, and Pi-hole’s own schedule is weekly. Running gravity more often downloads the same file again.
What does a free account load into Pi-hole?
The first 10% of each list. Gravity loads it without an error, so compare the entry count with the published count. The full list needs a Basic, Pro, or Enterprise plan.
Related
Threat domains as an authenticated DNS blocklist
Every list, level and category
TAXII 2.1 collections for SIEMs and TIPs
Authentication, endpoints and limits
Check one domain before you block it
Get Started
Ready to get started?
No credit card required · Free API key