Browsers & Extensions

The browser is the last component in the chain that can still say no

By the time a navigation begins, the mail gateway has passed the message, the messaging app has rendered the preview and the user has decided to trust it. Everything downstream of that decision happens inside your engine. This page is about building navigation-time blocking properly: a bundled local list, an interstitial that tells the truth, and a coverage claim you can defend.

390,000+Active phishing domains, each DNS-verified as resolving
Under 50msAPI response time, if you check remotely rather than locally
04:30 UTCDaily rebuild, shipped with an added and removed changelog
CSV or JSONFull database download, unlimited pulls on a subscription
Home / Use Cases / Web Browsers & Extensions
Where the decision happens

Every other layer gets a vote. The browser gets the last one.

That position is a privilege and a constraint. You see every navigation regardless of how it started — and you have milliseconds and no context in which to decide.

A phishing link can arrive through a route no security product in the chain will ever inspect. It can be typed from a phone call, scanned from a printed code, opened from a messaging app that renders links in its own view, pasted from a document, or followed from a search result that looked entirely ordinary. The organisational controls that people describe as "the phishing defence" — mail filtering, proxy policy, DNS enforcement on the corporate network — are each covering one path, and none of them covers a personal device on a mobile connection following a link out of a chat.

Every route ends in a navigation

Intent has already been formed, the URL is resolved, and a request is about to leave. That is the browser's moment, and the only moment in the sequence guaranteed to exist — which is also why it is a moment with almost no information available: no message body, no sender reputation, no thread history, nothing but a hostname and whatever local state you hold.

Which is why blocklists win at this layer

A hostname is available before a single byte has been fetched, it is cheap to look up, and it evaluates deterministically inside the time budget a navigation actually has. Anything cleverer — analysing page content, comparing rendered layout, scoring form fields — needs the fetch to have happened, which means the request has already left the device and the harvesting page already knows the visitor exists.

Three constraints, not one

The mechanism has to satisfy all of them simultaneously: it cannot leak the user's browsing to a third party, it cannot cost more than a few milliseconds, and it cannot occupy more memory than a browser process is willing to give a security feature. Those constraints — not detection quality — are what determine the architecture, and they all point the same way.

What the rest of this page covers

Why the list belongs on the device rather than behind a network call, how to size the structure it lives in, what belongs on the interstitial, how the bypass should behave, how extensions and managed deployments differ, and exactly what to say about the domains the list does not yet contain.

Navigation-time blocking, defined precisely

What it is: evaluating the destination hostname of a navigation, after the URL is resolved and before the network request is issued, against a list of hosts confirmed as credential-harvesting infrastructure — and replacing the navigation with an interstitial when a match is found.

What it is notContent inspection, heuristic scoring of page structure, or reputation. Those are different techniques with different trade-offs, and every one of them requires the page to have been fetched first. Navigation-time blocking is deliberately the crudest, earliest and cheapest member of the family.
Why it mattersBecause it fixes what you can honestly claim. A match means this hostname is confirmed hostile. No match means this hostname is not in the list — never that the page is safe. Every design decision below follows from taking that second sentence seriously.
Architecture

Six engineering decisions, and the one that determines the rest

Where the list lives dictates your privacy story, your latency budget, your offline behaviour and your failure mode. Decide it first.

Ship the list, do not query it

A remote lookup on every navigation sends a record of the user's browsing to a third party, adds a network round trip to the critical path, and fails open the moment connectivity does. All three are unacceptable in a browser.

Instead: download the full database from /feed on your own cadence, compile it into whatever structure your engine prefers, and match locally. Downloads are unlimited on a subscription, so update frequency stays an engineering decision rather than a billing one.

Size the structure honestly

Roughly 390,000 hostnames is a small dataset by browser standards, but not one you want as a naive string set in every renderer. A hash set of truncated digests, or a probabilistic filter backed by an exact check on a hit, keeps the resident footprint in the low hundreds of kilobytes.

One caveat: a probabilistic filter has false positives by construction and no false negatives, so a filter hit must always be confirmed against the exact set before you show an interstitial to anybody.

Normalise before you match

Lowercase, strip the trailing dot, convert internationalised labels to their encoded form, and decide explicitly how you treat subdomains. Matching only the exact host misses the most common pattern in this data — a brand name as a label under an unrelated registered domain.

The rule that matters most: apply identical normalisation to the list at compile time and to the navigation at match time. Divergence between the two is the classic source of silent misses, and it is invisible in testing.

Ride your existing update channel

The database rebuilds daily at 04:30 UTC and ships a changelog of additions and removals, which is what makes incremental updates practical. A component that pulls a diff is far kinder to a metered mobile connection than one replacing a full file.

Make staleness observable. A client that has not updated in a week should record that somewhere a support engineer can see it, because a silently stale list is the realistic failure mode of every shipped blocklist.

Design the interstitial for the truth

Say what is actually known: this hostname has been verified as an active credential-harvesting site, and the page was not loaded. Avoid the generic "attackers might be trying to steal your information" register — users have been trained to click through it precisely because it is so often wrong.

Show the hostname in a form that makes the deception visible, and give a route to report a mistake. The credibility of your warning is a finite resource, spent a little every time it appears.

Make the bypass deliberate and scoped

A proceed-anyway option is necessary — false positives exist and a browser that traps a user is worse than one that warns them — but it should require an extra interaction, not be the visually dominant button, and should apply to that host for that session only.

Build it policy-controllable. Enterprise deployments will want the bypass removable by administrative policy, which is worth supporting even if your consumer channel never uses it.
The path of one navigation

Five steps, all of them local, none of them on the network

This is the whole flow. Its brevity is the point — everything expensive happens at update time, not at navigation time.

Navigation starts

A click, a typed address, a redirect or a link handed over from another application. Intent is already formed.

Input

Host extracted

Parse the URL, take the host, normalise it exactly as the compiled list was normalised.

Microseconds

Filter probe

A probabilistic membership test against the in-memory structure. Almost every navigation ends here, cleanly.

In memory

Exact confirm

A hit is confirmed against the exact set before anything is shown, because a filter hit alone is not evidence.

No network

Interstitial

The request is never issued. The user sees what was blocked and why, with a scoped way to proceed.

Request suppressed

Notice what is absent. No outbound call, so no record anywhere of what the user was about to visit, no latency dependent on a network you do not control, and no behaviour change when the device is offline or behind a captive portal. That last case matters more than it sounds — a user joining unfamiliar public Wi-Fi is in one of the higher-risk situations a browser encounters, and it is exactly the moment a remote-lookup design is least able to answer.

The API still has a role — just not on the navigation path. Sub-50ms responses and a rate limit of ten requests per second per key suit build-time and tooling work: validating a candidate list during development, checking user-reported hostnames against /batch at a hundred domains per call, or confirming database size and last update through /stats, which needs no API key, no credits and no authentication. Keep the key server-side; it is the username chosen at registration and has no business inside a shipped client.

Extensions and managed deployments

The same list, three quite different distribution problems

An extension, a managed enterprise browser and a mobile browser each impose their own constraints on how the list gets in and stays current.

An extension has the tightest constraints and the least room to be clever. Store review will scrutinise both the permissions requested and any network traffic the extension generates, and an extension that phones home on every navigation is the archetype of what reviewers reject — reasonably, since that is indistinguishable from browsing surveillance. Bundling the list, or fetching it from a single well-documented endpoint on a slow schedule, is far easier to justify in a review and far easier to explain in a privacy disclosure. It also lets you request narrower permissions, because you need to observe navigations rather than read page content.

Packaging is the extension's real problem. Shipping the compiled structure inside the package means every update needs a store submission, which for a daily-changing dataset is impractical. The usual compromise is a small bundled snapshot so a fresh install is protected immediately, plus a background fetch of the current build so the shipped snapshot is only ever the floor — the changelog makes that fetch cheap, and the 04:30 UTC rebuild gives a predictable point to schedule around.
Managed deployments invert several assumptions usefully. An administrator can be given policy control over whether the interstitial may be bypassed, which hosts are permanently allowed regardless of the list, and where the list is fetched from. Enterprises with restricted egress will want it pulled once, centrally, into their own distribution system rather than fetched per client — and the feed being a plain downloadable file makes that trivial. The network-side pattern is on the DNS filtering page and endpoint-side coverage on endpoint protection.
Mobile sits between the two, with memory as the dominant constraint. A truncated-digest set is the usual answer, sized so the resident footprint stays negligible against a browser process, with the exact confirmation step reading from a compact on-disk structure rather than keeping the full set in memory. The daily diff keeps update payloads small enough not to be objectionable on a metered connection — the practical difference between a feature that stays enabled and one users switch off.
One note that applies to all three. If your product renders link previews, unfurls URLs or resolves shortened links before the user clicks, that is a second place the same check belongs — often more valuable than the navigation check, because it fires before the user has committed to anything. The messaging-side argument is on our messaging apps page and the shortener mechanics on URL shorteners.

The interstitial is a claim, and users audit it. A warning that says "this site may be dangerous" when the site is fine teaches people to click through, and the lesson generalises to every warning you will ever show them. A warning that says "this hostname was verified as an active credential-harvesting page and the page was not loaded" is a narrower claim, harder to be wrong about, and therefore worth more the tenth time a user sees it than the vague version is the first time.

Coverage, stated honestly

What this list catches, and what it does not

A browser vendor has to be able to describe its safety feature accurately, because somebody eventually will on your behalf.

Start with the inclusion rule, because it determines everything else. A domain is in the database only if it has been observed as phishing infrastructure and confirmed to have an active A record, verified through rotating proxy infrastructure. Hosts that stop resolving fall out rather than accumulating. That is why the active population sits around 390,000 rather than growing indefinitely, and it is why a match is unusually strong evidence: you are not looking at something reported once in 2023, you are looking at something that answered a DNS query within the last rebuild cycle.

Limit one: the same-day domain

A domain registered this morning and first used this afternoon will not be in this morning's build. There is no way around that for any list-based mechanism, and a browser implying otherwise is making a claim it cannot support. Infrastructure stood up and burned inside a single day is the residual risk of this entire technique, and it belongs in your security documentation rather than being glossed over.

Limit two: granularity

This is a domain-level dataset, so a hostile page on a compromised legitimate site — a path under a domain that is otherwise entirely benign — cannot be expressed by a hostname list without blocking the legitimate site too. That trade-off is not worth making, so the honest answer is that this technique does not cover that case at all. Page-level detection is a different mechanism with different costs.

Limit three: scope and the clean result

The category is phishing/malware and the confidence is 0.98 or 0.0 with nothing between — no reputation score, no risk band, no partial verdict, which is a simplification in your favour since there is no threshold to tune. But it also means a clean result carries no positive information at all, so any element rendering "not on the list" as a green tick is materially misleading.

Why it is worth shipping anyway

The overwhelming majority of what actually reaches ordinary users is not bespoke same-day infrastructure. It is industrialised, reused, and often several days old by the time a link has been forwarded, screenshotted, re-shared and finally clicked. A daily-verified list of live hosts intercepts a great deal of that at the cheapest point in the whole stack — no network call, no user decision, no data leaving the device.

Claim precisely. "Blocks known phishing sites, using a list of domains verified as actively hosting credential-harvesting pages, refreshed daily" is accurate, defensible and still compelling. "Protects you from phishing" is neither accurate nor, in the long run, persuasive.
Never render a clean result as a positive signal. No badge, no tick, no green shield. The absence of a match is the absence of information, and a safety indicator built on it will eventually be shown on a page that harms somebody.
Instrument the bypass, not the block. Block counts describe the adversary's activity. The proportion of interstitials users click through is the number that tells you whether your warning is still believed — and it is the one that degrades quietly.
Cost

What it costs to ship this in a client

For a browser or extension the answer is almost always the feed, because the alternative does not survive contact with the privacy requirement.

The Daily Threat Feed is $499 per month or $499/month — a third off the monthly rate. It includes unlimited downloads, which is the property that matters when you are distributing to clients: your update cadence, your staging environments, your build pipeline and your CI checks can all pull as often as they need without changing the bill. The annual tier adds historical archive access, priority support, custom format options, a dedicated account manager and 100,000 API credits.

What the bundled credits cover

Enough for the API-shaped work a browser team actually does, which is not navigation-time checking: validating user-reported hostnames, spot-checking during development, and screening the destination hostnames of anything you distribute — new-tab content, bundled bookmarks, partner links — through /batch at a hundred domains per call and one lookup each. Beyond the allowance, packages run from Growth at $99/month for 25,000 lookups at $0.0040 through Professional at $249/month for 100,000 lookupsup to Enterprise at $999/month for 750,000 lookups on the Enterprise plan, billed monthlyunder ten percent usage .

Two options to know at design time

Custom delivery — SFTP or S3 rather than an HTTPS pull — fits organisations whose build systems ingest third-party data through a fixed pipeline, and on-premise deployment exists for those who cannot depend on an external endpoint at all. Both are quoted rather than listed. Custom update frequency is also available, which matters if your client update channel runs on a cadence other than daily and you would rather align the two than carry a diff you cannot ship.

Something to point a monitor at first

The /stats endpoint reports current database size and last update time and requires no API key, no credits and no authentication, which makes it a reasonable target for a monitoring check while you evaluate. Full pricing detail is on the pricing page, delivery options on the daily feed page, and endpoint semantics in the API documentation.

Questions

What browser and extension engineers ask

Mostly about privacy, memory and what happens when the list is wrong — which are the right things to ask about.

Does this replace the browser's built-in safe browsing?

No, and stacking it as a second independent list is the sensible framing. Built-in protections in mainstream engines are broad and cover several categories; this is a narrow dataset of domains verified as currently resolving and confirmed as credential harvesting, rebuilt every 24 hours.

Two lists built by different processes miss different things. If you are building an alternative browser or a security extension, this gives you a dataset whose distribution you control — you are not dependent on another vendor's service being reachable, or on their terms for how it may be used.
How much memory does the list actually cost?

Less than teams expect, provided you do not keep raw strings resident. Around 390,000 hostnames stored as truncated digests in a hash set, or as a probabilistic filter with an exact confirmation step, keeps the resident structure in the low hundreds of kilobytes — small enough to sit in a browser process without argument.

The choice that actually costs memory is keeping the full string set resident in every renderer. Keep the compact structure in memory, the exact set on disk, and confirm only on a probe hit — which by construction is rare.
Can we check remotely instead of shipping the list?

Technically yes; the check endpoint responds in under 50 milliseconds. For a browser you should not, and it is worth being direct about why.

Four reasons not to: a remote check per navigation transmits the user's browsing history one hostname at a time to a third party; it adds a network round trip on the critical path; it fails open when the device is offline or behind a captive portal, which are precisely the higher-risk moments; and the rate limit of ten requests per second per key makes it structurally unsuited to client-side use anyway. Ship the list, and use the API for build-time and tooling work.
How do we handle subdomains and internationalised names?

Decide the rule explicitly and apply the same normalisation to both sides. Lowercase, strip any trailing dot, and convert internationalised labels to their encoded form before matching — otherwise visually identical hosts will compare unequal in ways that are extremely hard to debug from a user report.

On subdomains specifically: exact-host-only matching misses the most common shape in this data, where a recognisable brand appears as a label beneath an unrelated registered domain. Most implementations match the exact host and then walk up the label chain to the registrable domain, stopping at the public suffix — and whatever you choose, apply it identically at compile time and match time.
What happens when we block something legitimate?

Assume it will happen and build the path first. The interstitial should carry a report-a-mistake route, the bypass should be scoped to that host and that session, and enterprise policy should be able to hold a permanent allow-list applied after each list update so an override is not silently erased by the next refresh.

Why the path matters more than the rate: verification that the host is live and hostile keeps false positives low, but a user who hits one and cannot get past it or report it will disable the feature entirely — and then you have no protection at all rather than imperfect protection.
Should the extension show a positive indicator on clean sites?

No, and this is the single most common design mistake in this product category. A clean result means the hostname is not in the list — it carries no positive information at all. Rendering it as a green tick or a "verified safe" badge converts the absence of evidence into an assurance you cannot support, and that badge will eventually appear on a page that harms somebody.

Show nothing when there is no match. Reserve your interface surface for the case where you actually know something — the confirmed match — and let silence mean silence.
How fresh is a shipped list in practice?

That depends on your update channel more than on the data. The build completes daily at 04:30 UTC with a changelog of additions and removals; if your client pulls the diff once a day, worst-case staleness is about a day plus your propagation time.

The important engineering work is making staleness visible. A client that has failed to update for a week should record that somewhere diagnosable, and ideally soften its claim rather than continuing to imply current protection. Silent staleness is how list-based features stop working without anybody noticing.
We are a small extension developer. Is the feed subscription proportionate?

It depends entirely on whether you are shipping to users. If you are distributing a client that needs a current list, the feed is the only architecture that respects your users' privacy, and $499 a month is the cost of that architecture. There is no cheaper shape that does not involve sending browsing data somewhere.

If you are building tooling rather than a shipped client — a research utility, an internal checker, a batch validator — the credit route is proportionate and starts at Growth at $99/month for 25,000 lookups. Confirm current database size and last update through the /stats endpoint first, since it needs no key and no credits, then decide which shape your product actually requires.

Block the navigation on the device, before the request ever leaves it

Take the full database daily, compile it into your engine, and stop confirmed credential-harvesting hosts at navigation time — with no lookup traffic, no dependency on connectivity, and nothing about a user's browsing leaving the machine.