Design a Web Crawler at Scale
1. Problem Statement & Scope Clarification
System Mission
Design a planetary-scale, distributed, fault-tolerant Web Crawler (similar to Googlebot, Bingbot, or Common Crawl) capable of crawling web pages per month, strictly adhering to domain politeness policies and robots.txt protocols, eliminating duplicate URLs and near-duplicate content, and persisting standardized Web ARChive (WARC ISO 28500) datasets into Amazon S3 for downstream search engine indexing and LLM pre-training pipelines.
Functional Requirements
- Planetary-Scale Distributed Crawling: Crawl billions of web pages starting from a curated set of high-authority seed URLs, traversing outbound hyperlinks up to a bounded depth.
- Politeness &
robots.txtCompliance: Respect target website rate limits, crawl-delays, and disallow directives. Restrict crawl velocity to at most 1 request per second per target host unless explicit crawl-delay specifies otherwise. - Multi-Stage Deduplication:
- URL Deduplication: Prevent re-crawling visited URLs within a 30-day freshness horizon using high-speed in-memory Bloom filters.
- Near-Duplicate Content Detection: Identify duplicate or template-generated mirror pages using 64-bit SimHash Hamming distance ( bit differences).
- HTML Parsing & Link Extraction: Extract canonical URLs, page titles, text content, and outbound links, converting relative links to absolute, canonicalized URI representations.
- Standardized Web Archive Persistence: Package raw HTTP request/response payloads, headers, and metadata into compressed WARC files and stream them to Amazon S3 Standard-Infrequent Access (S3-IA).
Non-Functional Requirements (SLAs & SLOs)
- Throughput & Scale: Ingest ( sustained average, Peak: ).
- Politeness SLA: DDoS incidents against target websites; strict per-domain throttling.
- Compute Cost Efficiency: Resilient to sudden instance terminations using AWS EC2 Spot Fleets with automated state checkpointing ( compute cost reduction).
- Extensibility: Pluggable architecture supporting headless browser rendering (Playwright/Puppeteer) for JavaScript-heavy Single-Page Applications (SPAs).
2. Capacity & Scale Estimation (Back-of-the-Envelope Math)
Ingestion & Crawl Throughput Derivations
- Monthly Crawling Target: (1 Billion pages).
- Average Crawl Rate:
- Peak Crawl Rate ( burst multiplier for high-bandwidth domains):
- Average HTML Page Payload: (HTML text, stylesheets, embedded headers).
- Peak Ingress Network Bandwidth:
Storage Footprint (Raw vs Compressed WARC Archive)
- Monthly Raw Web Ingestion:
- Compressed Archive (Zstandard compression ratio ):
- Annual Storage Accumulation: Stored in Amazon S3 Standard-IA with lifecycle transition to S3 Glacier Deep Archive after 90 days.
Memory Sizing for URL Deduplication (Redis Bloom Filter)
To prevent re-crawling, we track () discovered URLs over a 30-day window with a false positive probability ():
- Optimal Bit Array Size ():
- Optimal Number of Hash Functions (): Using MurmurHash3 double-hashing (), an Amazon ElastiCache Redis Cluster with handles this deduplication dataset with room for growth.
EC2 Spot Crawler Fleet Sizing
- Average fetch latency per page (DNS + TCP/TLS Handshake + HTTP Stream): .
- Concurrently active HTTP socket connections needed for :
- Using
c6i.2xlargeSpot instances (8 vCPUs, 16 GB RAM, 12.5 Gbps network) capable of sustaining 150 concurrent non-blocking async HTTP worker coroutines: We provision a minimum of 16 Spot instances across 3 Availability Zones for N+4 redundancy.
3. High-Level Architecture & AWS Component Mapping
Synthesizing vector architecture diagram...
Mercator Two-Tier Architecture: Decoupling Priority from Politeness A single shared FIFO queue inevitably causes severe politeness bottlenecks or unintentional denial-of-service (DDoS) attacks against target domains. The Mercator frontier architecture strictly separates priority (quality-based ranking via PageRank into priority queues ) from politeness (rate-limited host isolation via host queues ), guaranteeing that high-value pages are prioritized without violating per-domain request rates.
Data Flow Walkthrough
- Frontier Enqueue & Deduplication: Discovered links are submitted to the Frontier Coordinator. The URL is canonicalized (lowercased, default ports stripped, query params normalized). It tests the Redis Bloom Filter; if present, the link is discarded immediately ().
- Priority Classification: Unvisited URLs are categorized by domain authority into Priority SQS FIFO queues ().
- Politeness Dispatching: The Politeness Router pulls URLs from priority queues using biased lottery scheduling and routes them into a single SQS FIFO politeness queue with
MessageGroupId = host_<domain>. SQS FIFO delivers the messages of one group to only one consumer at a time and in order, so the queue itself serialises each host; millions of hosts become millions of groups inside one queue, not millions of queues (SQS has no practical way to create or poll a queue per domain). A Redis lease (SET lease:host:<domain> PX 1000 NX) then spaces consecutive fetches to the same host at least 1 second apart, since group ordering alone says nothing about timing. - Optimized Network Fetching: The Crawler Worker queries its local Unbound DNS Cache (avoiding external DNS network round trips) and initiates an asynchronous HTTP/2 GET. The worker parses
robots.txtdirectives cached in memory. - SimHash Verification & Archival: The downloaded HTML is fingerprinted using a 64-bit SimHash. If Hamming distance to any known page on the domain is , it is flagged as a near-duplicate and skipped. Otherwise, the raw payload is written into an open WARC container in Amazon S3, and newly extracted outbound URLs are looped back to step 1.
Concrete Step-by-Step Request Walkthrough: Tracing a Discovered URL Crawl
| Step # | Event / Action | Component State | Distributed Transition | Output / Response |
|---|---|---|---|---|
| 1 | Parser extracts hyperlink:https://en.wikipedia.org/wiki/Distributed_computing | URL canonicalized to standard RFC 3986 format | Evaluated against Redis Bloom filter ( hashes) | Bloom returns 0 (Definitively new link);proceeds to priority ranking |
| 2 | Priority classification & ranking | Domain wikipedia.org matchesTier-1 Authority catalog | Routed into Priority Queue ( SQS_FIFO_HIGH) | Message enqueued with deduplication ID: |
| 3 | Politeness host allocation | Politeness dispatcher pulls from ; extracts host en.wikipedia.org | Enqueued with MessageGroupId = host_en.wikipedia.org;checks Redis lease lease:host:en.wikipedia.org | Lease available; distributed lock granted for |
| 4 | DNS resolution & robots.txt check | Worker queries local Unbound DNS cache daemon | Cache hit: 208.80.154.224 ();evaluates in-memory robots.txt AST | Route allowed; domain crawl-delay and path permissions verified |
| 5 | Streaming page fetch over HTTP/2 | Worker opens TLS 1.3 socket; streams HTML payload () | Worker checks HTTP status 200; streams bytes to memory buffer | Download completes in ; socket gracefully returned to pool |
| 6 | SimHash near-duplicate detection | Computes 64-bit SimHash fingerprint from weighted visible text tokens | Fingerprint 0x8F9A... evaluated againstDynamoDB table with Hamming distance | New unique content verified; 84 outbound links extracted |
| 7 | WARC compression & S3 append | Worker appends request/response record to active .warc.zst buffer | Buffer rotated at boundary; multipart streamed to S3 Standard-IA | Archived in S3 lake:chunk_104.warc.zst ( commit) |
4. API Interface Design & Control Protocol
Internal Crawler Worker Job Message Protocol (SQS FIFO Message Body)
json{ "message_id": "msg_984f1a2b-3c4d-5e6f-7a8b-9c0d1e2f3a4b", "crawl_job_id": "job_2026_09_crawl", "target_url": "https://en.wikipedia.org/wiki/Distributed_computing", "canonical_url": "https://en.wikipedia.org/wiki/Distributed_computing", "host": "en.wikipedia.org", "depth": 3, "max_depth": 8, "priority_score": 92.5, "retry_count": 0, "parent_url": "https://en.wikipedia.org/wiki/Computer_science", "scheduled_at_epoch_ms": 1767225600000 }
Crawl Result Event (Emitted to Kinesis Ingestion Pipeline)
json{ "url": "https://en.wikipedia.org/wiki/Distributed_computing", "http_status": 200, "content_type": "text/html; charset=UTF-8", "content_length_bytes": 430080, "fetch_duration_ms": 312, "simhash_fingerprint": "0x8f9a2b1c4d3e5f60", "discovered_links_count": 84, "warc_file_s3_uri": "s3://crawler-warc-lake/2026-09-16/chunk_104.warc.zst", "warc_record_offset": 41943040, "warc_record_length": 143360, "crawled_at": "2026-09-16T12:00:00.000Z" }
5. Data Models & Storage Architecture
1. DynamoDB Visited URLs & SimHash Registry (VisitedUrlsRegistryTable)
| Attribute Name | DynamoDB Type | Description |
|---|---|---|
PK (Partition Key) | STRING | HOST#<canonical_host> (e.g., HOST#en.wikipedia.org) |
SK (Sort Key) | STRING | URL#<sha256_url_hash> |
raw_url | STRING | Full original canonical URL |
simhash_64 | NUMBER | 64-bit integer content fingerprint |
http_status | NUMBER | Final HTTP status code (200, 301, 404, etc.) |
crawled_at_epoch | NUMBER | Unix epoch in seconds (TTL: 30 days) |
etag | STRING | Target web server HTTP ETag / Last-Modified, replayed as If-None-Match on recrawl (Section 6.4) |
next_crawl_at | NUMBER | Adaptive recrawl time derived from the observed change rate (Section 6.4) |
warc_pointer | STRING | S3 URI and byte offset reference |
2. S3 Web Archive (WARC ISO 28500) Storage Standard
Files are packaged into standardized chunks compressed with Zstandard (.warc.zst) partitioned by crawl date and target domain cluster:
| S3 key prefix | What it holds |
|---|---|
s3://crawler-warc-lake/year=2026/ | One prefix per crawl year |
…/year=2026/month=09/ | One prefix per month |
…/year=2026/month=09/day=16/ | One prefix per crawl day |
…/day=16/CC-MAIN-20260916-00001.warc.zst | First 1 GB Zstandard-compressed WARC file of the day |
…/day=16/CC-MAIN-20260916-00002.warc.zst | Second WARC file of the day |
…/day=16/CC-MAIN-20260916-00003.warc.zst | Third WARC file of the day |
Unlock Complete Architecture & Production Runbooks
You have explored the free architectural preview (~38%). Spend 1 Coin to unlock the remaining 6 production deep-dive sections for a full 24 hours.