Rebuilding North Korea's web from a dead DNS zone

The entire .kp country domain stopped resolving worldwide. This is how 37 of its websites were reconstructed anyway - from the Internet Archive, by walking each site's own link graph - and what the crawl exposed about how the country's web is actually shaped.

37 sites recovered84,841 URLs indexed 3 crawler bugs2026-10-21
37sites mirrored
1,255pages captured
5,752files
428 MBarchive size
7no archive exists
The brief: "build a local archive/copy of every publicly known North Korean website" - starting from one airline site that wouldn't open. It turned into a DNS investigation, a 44-host enumeration, and a crawler that had to be rebuilt three times.

1. The failure was DNS, not the server

The site that started this was airkoryo.com.kp. It produced a name-resolution error, which reads like "the site is down". It wasn't:

$ dig +short airkoryo.com.kp          # nothing, from every resolver
$ dig +short @8.8.8.8 airkoryo.com.kp  # nothing
$ curl --resolve www.airkoryo.com.kp:80:175.45.176.69 http://www.airkoryo.com.kp/
HTTP/1.1 200 OK   13,110 bytes   Air Koryo

Same server, reached by raw IP, answered in ~25 seconds at 0.5 KB/s. So the web server was alive the whole time; only the lookup was broken. That reframed the problem: this was not an outage to wait out, it was an address book that had been withdrawn.

Why the whole TLD disappears

The root zone still delegates kp. - that part is intact. It points at ns1.kptc.kp and ns2.kptc.kp, which live at 175.45.176.15 and .16, inside the same /22 block as the websites themselves. Probe those nameservers directly and the shape of the problem appears:

$ dig @175.45.176.15 kp.        +tcp   ->  NOERROR   (responds)
$ dig @175.45.176.15 kcna.kp    +tcp   ->  SERVFAIL  (children don't)
$ dig @175.45.176.15 kp.        +udp   ->  timeout   (UDP/53 filtered)
$ Cloudflare DoH  ->  EDE(22): No Reachable Authority at delegation kp

UDP/53 is filtered outright; TCP/53 answers only for the apex. Because the authoritative servers sit inside the very link that gets closed, resolvers worldwide cannot reach the authority for any child name - and when that happens, every .kp host vanishes simultaneously, not just the one you asked for. That is why it looked like "North Korea's internet went down" instead of "one site broke".

The domestic web is a separate thing entirely. Kwangmyong is an intranet: its own sites, mail, search and a mirrored encyclopedia, none of it routable, none of it archivable. The .kp slice on the public internet is the small foreign-facing edge of a system whose bulk has never been reachable from outside.

2. Enumerating what exists

No zone transfer is possible, so the host list had to be assembled from three independent sources: the public DPRK-website trackers, passive-DNS history, and institutions whose domains appear in archived links. That produced 44 candidate hosts. Then each was queried against the Wayback CDX index - twice, because the first pass used a prefix match that silently missed everything archived under www.:

   44 candidates
  → 37 with real captures        (84,841 URLs indexed)
  →  7 with nothing at all

The seven - kiyctc, knic, cooksfriends, korelcfund, ryongnamsan.edu, silibank, star-co - were never crawled by anyone. No archive can contain them; live capture is the only route and the door is shut.

3. Crawling each site's own link graph

The first mirror engine sampled URLs straight out of the CDX index and produced nonsense - /ar/, ?utm_source= fragments, login redirects. The second walked breadth-first from each site's real front page, following only links the site itself exposes. That is what makes the mirror browse like a site instead of a pile of pages.

for each host:
  seeds   = /, /index.html, www./, :80 variants, every netloc the CDX knows
  BFS     = 45 pages, depth 2, same-host only
  assets  = up to 220 per site, fetched 3-way parallel
  rewrite = strip web.archive.org wrappers, relativise /root-absolute refs
  verify  = re-parse every saved page, resolve each local ref against disk

Two details matter more than they look. Archive URLs come back with the port baked into the host (www.rodong.rep.kp:80), and pages are stored under their original web/<timestamp>id_/<url> wrapper - both have to be normalised or every link inside the mirror points back at the live archive instead of the local file.

4. The three bugs the crawl exposed

SymptomCauseFix
One site had 45 pages but a single file on disk Every article URL was index.php?newsID=...; the query was truncated to 36 characters, so all 45 IDs collapsed onto one filename and overwrote each other Hash-suffix the full query; 1 file became 79
Whole sites rendered with no CSS or images Host comparison used the raw netloc, so host:80 never matched host - every asset was rejected as off-site Compare .hostname only, never the netloc
Ten sites reported as "never archived" Archive.org signals rate-limits with a TCP connection refused, not a 429. The logger recorded that as an empty archive Treat connect-failure as throttle: back off 20-90 s, retry 4x

The third one is the one worth dwelling on. It produced a confident, wrong conclusion - "these sites don't exist in any archive" - for sites that turned out to be among the largest in the set, including the Pyongyang Times and Kim Chaek University of Technology. The mistake wasn't a crash; it was a log line that couldn't distinguish absent from busy. After the backoff fix, failed URLs per site dropped from 198 to 6, and coverage went from 27 sites to 37.

5. What the recovered web actually looks like

The domain space is a map of the state rather than a market. There is no e-commerce layer, no blogs, no shops - every site is an institution publishing on its own behalf:

SuffixSitesWhat lives there
.com.kp14trading companies, book and music sellers, the airline
.org.kp7friendship societies, funds, professional associations
.rep.kp5state organs - the news agency, party paper, radio, youth league
.edu.kp5universities
.gov.kp3ministries
.aca.kp / .sca.kp / .law.kp3 academy of sciences, arts collection, a law office
outside .kp3overseas solidarity groups on foreign domains

Two things follow from the capture dates. First, much of this is archaeology: the party newspaper's last snapshot is 2016, one of the big periodical archives stops in 2021, and a general-news site in 2024. The foreign-facing web peaked around 2010-2016 and has been going quiet on the same hardware ever since. Second, page counts are not article counts - the largest sites in the set are scanned-book and periodical libraries, where one "page" is a document record, not a story. The wire services are where the daily copy is.

6. Limits, stated plainly

The 45-page cap is the biggest caveat. Each site carries its front page and navigation, not its back catalogue. A site with 6,000 archived pages shows 45 of them. The cap is what made this tractable in hours instead of weeks - raising it is a rerun, not a rewrite.
Link resolution is 9,698 of 13,141 local references (73.8%). The remainder are overwhelmingly links to pages beyond the cap - an article page pointing at 2,000 sibling articles that were never captured. They are not corrupt files; they are the edge of the sample. Where an asset was never archived at all (four sites lost their stylesheets), the page still renders its full text, unstyled. Verified against the origin: the CSS genuinely 404s in the archive, so no placeholder was invented to paper over it.
Everything is verified by parsing, not by eye. Every saved page is re-parsed and each relative reference resolved against the filesystem. The portal's own numbers are read from the manifests the crawler wrote, so no figure on this page was typed by hand.

7. Evidence

MeasureValueHow it was checked
Candidate hosts enumerated44trackers + passive DNS + archived links
Hosts with captures37CDX matchType=domain, throttle-aware retries
Hosts with no archive74 attempts each, all empty (not refused)
URLs indexed84,841inventory.json
Pages saved1,255per-site _manifest.json
Files on disk5,752find over the mirror tree
Local refs resolving73.8%re-parse + filesystem resolve, per page
Live door statusshut14/14 probes to the /22 on 80/443 refused

8. What I'd do next

Three obvious continuations, in the order they'd pay off. A door watcher: the link opens occasionally - one 13 KB fetch of the airline site succeeded - so a low-frequency prober could grab live copies of the seven unarchived hosts the moment they answer. Deep mode on the sites that matter most, raising the cap from 45 pages to the full index for the wire services and the party paper. And full asset capture for the four sites missing stylesheets, if the archive ever backfills them.

The whole pipeline - recon, crawl, rewrite, verify - is documented and rerunnable. The archive this page describes is browsable at the portal.