Skip to main content
Web Crawlerlesson 4 of 4 · 3 min read

Deduplication and Storage

Two levels of deduplication

Run deduplication at two levels, and do not conflate them, because that is a common interview stumble.

One asks whether you have queued this address before, and it guards your frontier. The other asks whether you have stored these bytes before, and it guards your store, catching mirrors, session variants and the same article at ten addresses.

Treat address deduplication at a billion addresses as a memory problem. A set of a billion of them at sixty-odd bytes each is over 60 gigabytes and growing.

Reach for a bloom filter, the standard answer. At ten bits per element, a billion addresses fit in about 1.2 gigabytes of memory with a false positive rate near one percent.

Read the trade explicitly. A false positive means your crawler believes it saw an address it never saw, so that page is skipped forever.

For a search crawl, where silently missing one candidate in a hundred is fine. If it is not fine, back the filter with an exact store and let the filter reject cheaply first, keeping 99 percent of your lookups off the disk.

Content deduplication

Deduplicate content by hashing, at increasing sophistication. Exact duplicates fall to a hash of the body kept in a fingerprint store.

Catch near-duplicates, the same article with a different sidebar and timestamp, with similarity hashing. One well-known pipeline used a scheme mapping similar documents to fingerprints differing in only a few bits, so finding near-duplicates becomes a distance lookup.

Relax about storage, the calm part here. Raw markup is immutable blob data, so write it to object storage compressed, since markup squeezes five to one or better. Bundle it into the archival format the public web archives publish.

Put your metadata somewhere queryable: the address, the fetch time, the status, the content hash, the outgoing links. Deciding when to fetch a page again is tomorrow's problem, and it runs on that table.

the shape of it
New addressSeen filter1.2 GB for a billionFetchContent hashObject store1. seen before?2. no: fetch3. same bytes?4. new: keep
step 1 of 4
Two different checks: one guards the queue, the other guards the store.

Worked example

Common Crawl, the nonprofit whose dataset trains half the large language models in existence, shows the storage math at full scale. Each monthly crawl fetches on the order of 3 billion pages and publishes them as WARC files on S3, with each crawl adding roughly 90 TB compressed, several hundred terabytes raw. Their published stats also make the dedup case: crawl reports regularly show a meaningful fraction of fetched URLs resolving to duplicate or near-duplicate content, which is why fingerprints ship alongside the archives. A team at a startup fine-tuning models learned the lesson downstream: training on two months of Common Crawl without dedup meant the same boilerplate pages appeared thousands of times, skewing the model until they ran fingerprint-based filtering, the same simhash-family technique the crawl side uses.

Web Crawler: wrapping up

In the real world

  • 01Googlebot fetches from thousands of machines, publishes its crawler IP ranges, honors robots.txt, and assigns each site a crawl budget that adapts downward when servers slow or error, politeness as a formal, documented system.
  • 02Common Crawl fetches roughly 3 billion pages per monthly crawl and publishes them free as WARC files on S3, hundreds of terabytes per crawl, and that corpus became foundational training data for modern language models.
  • 03The Internet Archive's Heritrix crawler is open source and implements the frontier-fetcher-dedup architecture directly; reading its docs is the closest thing to a production answer key for this interview question.
  • 04The Mercator crawler paper from Compaq's research lab (1999) introduced the front-queue and back-queue frontier design, with per-domain back queues enforcing politeness, and it still underlies how the frontier is taught and built.
  • 05Google reported using simhash fingerprints to detect near-duplicate pages during crawling, turning fuzzy document similarity into a cheap Hamming-distance comparison across billions of fingerprints.

Questions people ask

Why is a bloom filter acceptable for URL dedup if it has false positives?

Because the failure is bounded and cheap: a false positive means one URL is wrongly considered seen and never crawled, roughly 1 percent of candidates at typical sizing. For broad crawls that miss rate is invisible in coverage terms. Crawls that need exactness pair the bloom filter with an exact store, using the filter to eliminate the vast majority of lookups before touching disk.

How does a crawler avoid infinite crawler traps?

With layered limits rather than one clever detector: maximum URL depth and length, a per-domain page budget, URL normalization that strips session IDs and boils away infinite query variants, and content fingerprinting that notices when a domain keeps serving near-identical pages. Traps still get partially crawled; the caps ensure they waste a bounded slice of the budget.

What actually limits crawl speed, the crawler or the web?

Per-domain politeness, almost always. Aggregate fetch capacity is easy to add with more async workers, but a domain crawled at 1 request per second yields at most 86,400 pages a day no matter how large your fleet is. That is why coverage math has to be done per domain, and why big sites get supplementary channels like sitemaps and change feeds.

Quick review

Seed URLs → URL Frontier (priority queue) → Fetch → Parse HTML → Extract links → Deduplicate → Add to frontier
URL deduplication:
bloom filter to reject already-seen URLs (probabilistic, memory-efficient). Exact-match DB for confirmed visited
Politeness:
robots.txt compliance. Per-domain rate limiting. Don't hammer one site with parallel requests
DNS caching:
resolve each domain once, cache for TTL. DNS is a bottleneck at scale
Distributed:
partition URL frontier by domain hash across crawler workers. Each worker owns a domain subset
Storage:
download raw HTML to object store (S3). Extract and index in separate pipeline (Spark, Flink)
Recrawl priority:
high-priority pages (frequently changing: news, prices) crawled more often via priority queue
the trade-off

Aggressive crawling gets your IPs blocked. Too polite = slow coverage. Bloom filter has false positives (some URLs skipped).

in the room

Tests distributed queues, deduplication at scale, politeness constraints, and pipeline architecture.