What a local weak-password list can prove—and why it is not a web-wide breach lookup

A checker that says “not on the weak-password list” is easy to misread as “this password has never shown up in any dump.” Those are different claims. A short list downloaded with the page can only answer whether the string looks like a password people already overuse. Have I Been Pwned answers a different question, and it uses a different data path. The rest of this note narrows the sentence you can write, and gives a check you can finish in DevTools.

A miss is not “never leaked”

Most local checkers return one of two results: a hit, or a miss. A hit is easy to use. The string already sits on a public frequent-password list, so keeping it means you stay in the first batch an attacker tries. A miss is where people add a second claim: the list came from “leaks,” so a miss must mean “clean.”

A list can only speak for the rows it contains. An unpublished internal dump, an infostealer log that no public corpus has folded in yet, or a password stolen from one small site and never republished will not appear in the few hundred or few thousand lines a tab just downloaded. The honest reading of a miss is: under this local list and this matching rule, the exact string—or the few variants the script knows how to try—was not found. That sentence never promised “the whole web.”

Write that limit down before you decide what to do next. A hit is a reason to change the password. A miss still leaves length, reuse, and whether a password manager generated the string. The list is a filter. It is not a clean bill of health.

What kind of guessing the list is for

Attackers rarely walk from a to zzzz…. The two common paths are credential stuffing—trying a leaked username and password on other sites—and online guessing that starts at the top of public weak-password rankings. 123456, password, and qwerty sit at the front of those rankings because people keep choosing them, not because they “look short.” A few tries often succeed.

A frequent-password list targets that layer. It marks strings that are already overused, while you can still change them. It does not answer how large the search space is, and it does not answer whether some unpublished corpus already has this exact row. Those are entropy estimation and a much larger breach corpus. They should not be collapsed into one verdict with “does this look like a common weak password.”

That is also why a list that is not especially long can still be useful. Online logins are almost always rate-limited. An attacker has a small budget before the account locks, so they try the highest-ranked passwords first. NIST SP 800-63B-4 says the point of a blocklist is to stop passwords that are likely to be guessed before throttling kicks in. Once the list is large enough for that goal, extra rows add little, because the online attack is already being throttled. A local frequent-password list is built for that goal. It is not designed to contain every password that has ever leaked.

NIST wants a full-string check, not extra complexity rules

The U.S. National Institute of Standards and Technology, in SP 800-63B-4, writes the comparison as a required step for the party that accepts a password: when a password is created or changed, the entire candidate must be checked against a blocklist of commonly used, expected, or compromised passwords. The check is not “does this contain a dictionary word” and not “is a symbol missing.” A hit must be rejected, and the user must be told why.

The same section rewrites the password policy that has been misunderstood for more than a decade. Verifiers must not require a mix of uppercase, digits, and symbols. They must not force periodic password changes unless there is evidence that this authenticator was compromised. A password used as the only factor must be at least 15 characters; as one factor in multi-factor authentication, the floor is 8. Those sentences are aimed at sites that accept passwords, not at a self-check page in a browser. They still help anyone reading a checker result: ticking complexity boxes is not proof of safety. The first thing worth blocking is a full string that already sits on a public list.

NIST also reminds implementers to give guidance after a rejection, so users do not stop at rewriting password as Password1. If a local list also tries case folding, two or three trailing digits, and common leetspeak (@ for a, 0 for o), that is the rewrite it is aiming at. It is still a heuristic, not a cryptographic proof. P@ssw0rd2026! may be caught. A rewrite the rules do not encode will not.

Do not run this check with a password you still use. If you need to compare a list, use an old password you have already retired, or a disposable test string. You are checking the rule and the traffic, not exposing a live credential again.

A short local list, a hash-prefix query, and a full offline corpus

“Did this leak?” is at least three different methods. Mix them and a miss becomes “never leaked.” The difference is not the slogan. It is which data leaves the device, and what a server can see.

The first method is a short local list. The page downloads a public text file into the current tab, the script builds a set in memory, and it looks up the full string you typed plus a few variants. The outbound request is the list file, not the password under test. Coverage equals the number of non-empty lines. Take the leaked-top10k.txt file MakePwd ships with the checker as an example. The file name says top10k. Open the response and count the lines yourself—today that file has a few hundred more than 800 frequent entries, not 10,000, and not the whole web. If the list fails to load, the implementation falls back to a tiny built-in set (for example password and 123456). Coverage shrinks. The check still stays on this device.

The second method is the Have I Been Pwned Pwned Passwords range query. Official docs describe the corpus as hundreds of millions of passwords that have appeared in breaches. The client hashes locally with SHA-1 (or NTLM) and sends only the first five hexadecimal characters to https://api.pwnedpasswords.com/range/{prefix}. It then compares the returned suffixes with the full hash it already computed. By design, neither the plaintext nor the complete hash is sent. That is k-anonymity: the server only learns which of about one million prefix buckets you landed in (165). It is still an external query. The thing queried is not the password itself. In November 2025, HIBP ingested about 1.3 billion unique passwords from the Synthient credential-stuffing corpus, of which about 625 million were new to that service. In June 2026 it added about 124 million unique passwords from stealer logs. Those numbers show the corpus growing. They do not mean “checked once, clean forever.”

The third method is to download the full corpus onto your own machine or network and compare offline. HIBP publishes that download as well. Coverage is close to the second method, but no prefix is sent to api.pwnedpasswords.com. The cost is updates, storage, and the hash index you maintain.

Method What leaves this device What a miss can support
Local frequent-password list The list file; the password under test should not Not on this list (or the variants the script implements)
HIBP range query The 5-character SHA-1 / NTLM prefix Not in the current Pwned Passwords corpus
Full offline corpus No lookup request Not in the copy you downloaded

None of those three sentences can be rewritten as “this password has never appeared in any breach.” Unpublished dumps, logs that have not been ingested, and a password you never handed to a third party sit outside every collection’s duty. A local list is the narrowest of the three. Its advantage is that the password under test does not have to leave the tab.

What a hit and a miss can each support

A hit can be written in a hard sentence: this string already sits on a public frequent-password list, or it is only a rewrite the list can recognize. Keep using it and you stay in the set credential stuffing and online guessing try first. The right move is to change it, and not to reuse the same string on other sites.

A miss must carry a scope. It supports “this does not look like a common weak password on this list.” It does not support “it was never dumped,” “it is safe to reuse everywhere,” or “the entropy is high enough.” A 20-character random password will miss. A password that only appeared in one unpublished dump will also miss. The chip on the page looks the same. The meaning does not.

There is also the case that looks long and should still be marked weak. A list hit outranks entropy. password is eight letters, so a charset estimate produces a non-zero bit count, and everyone still tries it first. If a local checker forces the grade to weak after a hit, the reason is the list, not a broken bit formula. The reverse is also true. A miss plus high entropy only supports “it does not look common, and brute force looks expensive.” That is not “already safe.” Reuse, phishing, and a site that stores plaintext are outside this comparison.

The shortest sentence you can send a colleague: a hit means change it now; a miss only rules out this list. Next, ask whether it is reused, and whether it was generated at random.

Check it on the spot: the list file, then Network

“Compared locally, nothing uploaded” cannot prove itself. What you can see on the spot are two things: which list file the browser downloaded, and whether the string you typed left as business data.

Start with the list. Open DevTools Network, filter for a request whose name contains leaked or top, and save the response as text. Count the non-empty lines in an editor. Spot-check the first few rows for public frequent passwords such as 123456 and password. The line count you verified is the denominator behind “miss.” Do not write “10,000 entries” because the file name contains 10k.

Then check the password under test. Prepare a canary that will never appear in real work, for example canary-audit-2026-x7k, and type it into the checker. Enable Preserve log, paste that string into the filter. It should not appear in an XHR / Fetch request line, query string, or body. It should not appear in an analytics query or body either. Static assets, fonts, and the list file may appear—the list leaving the origin is expected. The password under test leaving is a fail.

  1. Open Network, enable Preserve log, finish one check, then find the list-file response and count the lines.
  2. Run the check again with a canary password and search the filter for that string. A hit on any request means plaintext left the tab.
  3. If there is no hit, open analytics requests and confirm that titles and paths may appear, but the full input-box text must not.

The claim this supports is narrow: in this run, the password under test did not leave the current tab as an observed HTTP field, and list coverage equals the lines you counted. It does not prove an extension did not read the input box, and it does not prove next week’s script will behave the same. Change browser or version, and it is worth searching for the canary again.

Entropy and the list answer different questions

A checker page often shows a grade, a bit count, and whether the list hit. Treat them as one score and you will be confused when the bits look fine but the list hits, or when there is a miss but the string is short. They answer different questions. They should not cancel each other.

Entropy asks: if an attacker brute-forces the charset, how long does that take on average? A common formula is length times log2(charset size), then a discount for repeated characters or long runs of the same character. If the thresholds are written as ≥ 80 bits for very strong, ≥ 60 for strong, ≥ 40 for medium, and the rest weak, those are bins for this estimator. They are not laboratory crack times. Offline and online timelines also use different guess rates—for example 1010 guesses per second offline and 103 online, then the average case (half the search space). Change the rate and the time changes. The password did not.

The list asks: is this string already publicly overused? It does not use the bit count. A list hit should still be marked weak even if it happens to contain uppercase and a symbol. A six-character miss will have low entropy, and length alone will push it to weak—that has nothing to do with a breach corpus. A “weaker than it should be” warning below 8 characters is a length warning, not a leak-database warning.

So a complete reading looks at least three places: whether the list hit, whether the string is too short, and which entropy bin it landed in. Only after those three can you say “what this string looks like right now.” That still does not replace a password manager, and it does not replace “one random password per site.”

Use an open-to-use checker to practice the boundary

If you want a page that writes the boundary in the open, start with MakePwd Password Audit. It opens with no sign-up and no password vault. The product copy says the password under test is not uploaded and is not written to analytics. Matching happens against a public frequent-password list downloaded into this tab. It is not Have I Been Pwned, and it is not a web-wide stuffing check. A hit forces the grade to weak. Past 128 characters, the implementation does an exact match only and skips trailing digits and leetspeak.

Practice with a disposable test string, not a live password. Keep Network open: you should see the list file and maybe analytics, not the full input-box text. When you need a new password, the generator is also free to open. Random mode is 6–128 characters, default 16, and strings shorter than 8 get a weaker-than-it-should-be warning. Generation and the check both finish in the browser. Plaintext stays on this device by default.

The point of the drill is not to prove any one site “searches the most.” It is so you can tell a colleague three sentences: I counted the list lines; I searched Network for a canary; on a miss I only wrote “not on this list.” On any other page that claims a “local weak-password check,” the same steps still apply.

Three things to remember next time

First, a hit means change it; a miss only rules out the current list—do not read it as never leaked. Second, a short local list, HIBP’s five-character hash prefix, and a full offline corpus are three different data paths, and the meaning of a miss follows the path. Third, do not let entropy and the list cancel each other—a short password can miss, and a long but common password can hit.

If the next question is whether plaintext left this tab as business data, read How to verify browser encryption: prove plaintext never left. This piece only turns “what does the weak-password list cover” into a sentence you can write down.