Skip to main content

Setup guide

AdGuard Home blocklist of malicious domains, as an authenticated DNS blocklist

AdGuard Home fetches lists with Go’s HTTP client, which sends the credentials in a list URL as HTTP Basic, so the full isMalicious list loads. Set the update interval to 12 hours to follow the rebuilds.

No credit card required · Free API key

Data path
  1. isMalicious

    api.ismalicious.com

    blocklist-domains-critical-adguard.txt

    Rebuilt every 12 h

    1. Relay

      Step 6

      Keep the key out of AdGuard Home (optional)

  2. AdGuard Home

    Step 3

    Add it as a custom blocklist

On this page08

What you get

AdGuard Home filters names, so only the domain lists apply. The adblock form, ||domain^, blocks each domain and its subdomains; load one form per tier.

ListEntriesRebuiltPlans
blocklist-domains-critical-adguard.txtDefault. Domains listed by 6 or more threat sources, or 3 or more with a critical category, with their subdomains.about 2 millionevery 12 hBasic, Pro, and Enterprise
blocklist-domains-c2-adguard.txtCommand-and-control domains reported by C2 trackers, whatever their level.about 22,000every 12 hBasic, Pro, and Enterprise
blocklist-domains-ransomware-adguard.txtDomains in the ransomware category, whatever their level.about 3,600every 12 hBasic, Pro, and Enterprise
blocklist-domains-critical.txtThe critical tier in domains-only syntax: exact names, no subdomains. Load it instead of the adblock form, not with it.about 2 millionevery 12 hBasic, 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.

Compare plans

What a download returnsHTTP

GET https://api.ismalicious.com/blocklist/download/blocklist-domains-critical-adguard.txt

Full list or 10% sample

FieldBasic, Pro, and EnterpriseFree
X-Blocklist-Version:fulllite
X-Blocklist-Percentage:10010
Total entries:<COUNT><COUNT> (Lite Version - 10% of <TOTAL>)

First lines of the file

! IsMalicious.com Blocklist - Domains (Critical)
! Format: AdGuard
! 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

  • AdGuard Home v0.107.0 or later.
  • The AdGuard Home admin login, for the API or the configuration file.
  • An isMalicious API key and secret, from Account › API access.
  • Outbound HTTPS (TCP 443) over IPv4 from AdGuard Home to api.ismalicious.com, which has no IPv6 address.

Set it up

  1. 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.
  2. Build the list URL

    Put the key, a colon and the secret before the host, followed by @. Keys and secrets are UUIDs, so they need no encoding. Use api.ismalicious.com: the ismalicious.com edge refuses some sources with a 403.
    List URLURL
    https://<API_KEY>:<API_SECRET>@api.ismalicious.com/blocklist/download/blocklist-domains-critical-adguard.txt
  3. Add it as a custom blocklist

    Filters › DNS blocklists › Add blocklist › Add a custom list. Enter the name isMalicious critical, paste the URL in Enter a URL or an absolute path of the list, then Save. AdGuard Home downloads the list at once and reports its rule count.
  4. Set the update interval to 12 hours

    Settings › General settings › Filter update interval: 12 hours. The options are Disabled, 1 hour, 12 hours, 1 day, 3 days and 7 days; the setting saves on change. The default is 1 day, and the interval applies to every list you load.
  5. Or script it (optional)

    The /control API takes the same list and interval with the admin login; run it on the AdGuard Home host, where the interface answers over plain HTTP unless encryption is configured. In AdGuardHome.yaml, merge the excerpt into the existing filters and filtering keys with AdGuard Home stopped: a second filters or filtering key stops AdGuard Home from starting, and it overwrites changes made while it runs.
    AdGuard Home APIsh
    # On the AdGuard Home host: the interface is plain HTTP unless encryption is
    # configured. curl asks for the admin password, so it stays out of ps.
    AGH=http://127.0.0.1:<ADMIN_PORT>
    
    # body.json (mode 600), written with an editor, holds the list with the key:
    # {"name":"isMalicious critical","url":"https://<API_KEY>:<API_SECRET>@api.ismalicious.com/blocklist/download/blocklist-domains-critical-adguard.txt","whitelist":false}
    curl -fsS -u '<AGH_USER>' -H 'Content-Type: application/json' \
      -X POST "$AGH/control/filtering/add_url" --data @body.json
    
    curl -fsS -u '<AGH_USER>' -H 'Content-Type: application/json' \
      -X POST "$AGH/control/filtering/config" --data '{"enabled":true,"interval":12}'
    AdGuardHome.yaml (excerpt to merge)yaml
    # Excerpt to MERGE into the existing keys, with AdGuard Home stopped:
    # never add a second filters: or filtering: key; keep filter ids unique.
    # v0.107.36 and older: filters_update_interval sits under dns:.
    filters:
      # ...your existing entries...
      - enabled: true
        url: https://<API_KEY>:<API_SECRET>@api.ismalicious.com/blocklist/download/blocklist-domains-critical-adguard.txt
        name: isMalicious critical
        id: 1700000001
    filtering:
      # ...existing keys stay; change only:
      filters_update_interval: 12
  6. Keep the key out of AdGuard Home (optional)

    • AdGuard Home stores the URL in clear in AdGuardHome.yaml, shows it in the DNS blocklists table and prints it when a refresh fails.
    • To avoid that, fetch the list with the root-only script below, 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 the file’s absolute path instead of the URL.
    • AdGuard Home reads the file again when the list is next due, or on Check for updates.
    • Since v0.107.53 the file must sit in a directory that filtering.safe_fs_patterns matches: userfilters/ in the work directory for a configuration made by the install wizard, data/userfilters/ for one migrated from an older release.
    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 every 12 hours. /etc/ismalicious/netrc (mode 600) holds the key.
    # DIR must match filtering.safe_fs_patterns; on Docker, use the host directory
    # mounted on /opt/adguardhome/work, plus /userfilters.
    set -eu
    NETRC=/etc/ismalicious/netrc
    DIR=/opt/AdGuardHome/userfilters
    FILE=blocklist-domains-critical-adguard.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 in /etc/cron.d/ismalicious-adguard:
    # 7 5,17 * * *   root   /usr/local/sbin/ismalicious-fetch.sh
    # List path in AdGuard Home: /opt/AdGuardHome/userfilters/blocklist-domains-critical-adguard.txt

Verify it works

  • Filters › DNS blocklists shows the list’s Rules count and Last time updated.
  • Rules count matches the count of the plain file of the same tier in /blocklist/stats, give or take one rebuild; the stats list plain files only. About a tenth of it is the 10% sample.
  • Last time updated is not proof of success: it moves on a failure too when another list updated in the same pass. Look for updating filter errors in the log rather than trusting the date.
  • Filters › Custom filtering rules › Check the filtering with a domain from the list names the list and the rule that matched.
  • In the Query log, blocked queries show the list name.
Checkssh
# Rules count and last update of every list (curl asks for the password):
curl -fsS -u '<AGH_USER>' "http://127.0.0.1:<ADMIN_PORT>/control/filtering/status"

# The list itself, as root with the netrc file: no "Lite Version" in its header.
sudo curl -sS --netrc-file /etc/ismalicious/netrc "https://api.ismalicious.com/blocklist/download/blocklist-domains-critical-adguard.txt" | head -12

Troubleshooting

“got status code 401, want 200”

The key or the secret is wrong or incomplete. The message appears when you add the list, and as an updating filter log line on a scheduled refresh; both print the URL, credentials included, so clear them from logs you share. The previous copy stays active. 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 rules load

The URL carries no credentials, or the plan is Free or lapsed: past due, unpaid, canceled, incomplete or paused counts as Free. Compare the rules count with /blocklist/stats.

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; the next run tries again.

A timeout or a 502

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.

“cannot read more than 268435456 bytes”

AdGuard Home v0.107.78 and later refuse a list larger than filtering.max_http_size, 256 MB by default: adding it fails, or a refresh fails and the previous copy stays. Use a smaller tier rather than raising the limit. Older releases have no cap.

503 Service Unavailable

The list is being built for the first time, and the response carries Retry-After: 600. AdGuard Home ignores it and keeps the previous copy.

The list does not reload after you fix the cause

When other lists refreshed at the same check, AdGuard Home marks ours as attempted and waits a full interval. When every due list fails, it doubles its check interval from 1 hour, with no ceiling: 2 hours, 4, 8… Click Check for updates once the cause is fixed.

“data is HTML, not plain text”

A page answered with a 200 instead of the list: a captive portal, a proxy or a TLS inspection page. An edge refusal from an ismalicious.com mirror is a 403 instead (“got status code 403, want 200”). Use api.ismalicious.com, and exempt it from TLS inspection.

“likely binary character”

The header lines are not the cause: AdGuard Home skips # and ! lines. A control character means a corrupted download: run Check for updates again.

x509: certificate signed by unknown authority

AdGuard Home always verifies the certificate. Update the system CA bundle (on Entware routers, /opt/etc/ssl/certs), check the clock, and exempt api.ismalicious.com from TLS inspection.

“net/url: invalid userinfo” or “bad http(s) url”

Leftover brackets of the placeholders give “net/url: invalid userinfo”: remove them, and any space. “bad http(s) url” is a scheme error, or a file:// address: a local list takes a plain absolute path.

Limits

  • AdGuard Home keeps the URL, credentials included, in AdGuardHome.yaml, in the DNS blocklists table and in its filter status. It prints it in the log when a refresh fails and in the error when adding the list fails. The local-file variant of the last step avoids that.
  • One update interval covers every list: 12 hours also applies to the other lists you load.
  • Only the adblock form blocks subdomains; the plain and hosts forms block exact names. No wildcard entries are served, and AdGuard Home needs none: ||domain^ covers the subdomains.
  • AdGuard Home v0.107.78 and later refuse a list past 256 MB (filtering.max_http_size). blocklist-domains-all.txt is about 386 MB in plain form, so do not load it.
  • AdGuard Home is not a firewall: it can block a DNS answer by its address, but a bare IP line in a list is a pattern, not an address to block. Do not load blocklist-ips-* lists; use a firewall for those.
  • The 10% sample is the first tenth of an unsorted file, not the riskiest tenth.
  • No RPZ and no CIDR are served; AdGuard Home needs neither for domain lists.

Questions

Can AdGuard Home send an API key for a blocklist?

Yes. Put the key and the secret before the host in the list URL, separated by a colon and followed by @. AdGuard Home fetches the list with Go’s HTTP client, which sends them as HTTP Basic. The URL is stored in clear in AdGuardHome.yaml; a local file fetched by a script keeps the key out.

Which file blocks subdomains?

The -adguard.txt form. AdGuard Home reads its ||domain^ rules as the domain and all its subdomains. The plain and hosts forms block the exact names only.

How often does AdGuard Home refresh the list?

At its filter update interval, 1 day by default, which applies to every list. Set it to 12 hours: the lists are rebuilt every 12 hours, so refreshing more often downloads the same file again.

Can AdGuard Home use the IP lists?

Load only the domain lists. AdGuard Home is not a firewall: a bare IP line in a list is a pattern, not an address to block. Block IP addresses on a firewall.

What does a free account load into AdGuard Home?

The first 10% of each list. AdGuard Home loads it without an error, so compare the rules count with the published count. The full list needs a Basic, Pro, or Enterprise plan.

Get Started

Ready to get started?

No credit card required · Free API key