Katana is ProjectDiscovery’s crawler voor endpoint-ontdekking: een Go-binary met MIT-licentie dat een target neemt en URLs en endpoints teruggeeft voor de volgende tool in een pipeline. Het werkt in een browserloze HTTP-modus of met -headless, waarbij Chromium wordt aangestuurd. In de officiële documentatie wordt headless neergezet als de optie met de meeste dekking; deze test laat zien dat het type endpoint minstens zo belangrijk is als het aantal.
Ik bouwde een kleine site met drie bewust verschillende endpointcategorieën en testte welke modus elk van die categorieën vond in v1.6.1 met -d 4. Gewone HTML werd in alle vier configuraties volledig gevonden: 4/4 links en de volledige keten van drie hops. Het verschil zat in endpoints die via JavaScript-broncode of via runtime-wijzigingen in de DOM zichtbaar worden.
In deze test vond headless de runtime-DOM-categorie die de browserloze modi misten, terwijl standaardmodus met -jc de JavaScript-bestands-literals vond die beide headless-runs misten. Geen enkele rij in de matrix van vier commando’s dekte beide categorieën af. Scope, resume en known-files leverden de andere praktische grenzen.
Wat katana eigenlijk is
De katana-crawler — projectdiscovery/katana op GitHub — is geschreven in Go en heeft een MIT-licentie. Ik testte v1.6.1 op 27 juli 2026; die versie is relevant, omdat de bevindingen hieronder over dekking en known-files specifiek aan deze build zijn gekoppeld.
Die categorisering is hier extra belangrijk. Een endpoint-discovery crawler is geen veld-extractor. Als je een Go web crawler zoekt die productnamen en prijzen als gestructureerde JSON teruggeeft, zit je met katana in de verkeerde hoek. Katana meldt je graag dat /products/1138 bestaat, maar zegt niets over wat er op die pagina staat. Dat is ook precies de bedoeling; beoordelen op extractie zou zijn alsof je een metaaldetector recenseert op hoe goed hij sieraden taxeert.
Het domein waar katana wél in thuis is: offensieve security-recon en automatiseringspipelines. STDIN erin, URLs eruit, door naar de volgende tool. Daarmee komt meteen de voor de hand liggende waarschuwing: alle metingen hier zijn gedaan tegen een fixture op 127.0.0.1 die ik zelf schreef. Gebruik katana alleen op hosts die van jou zijn of waarvoor je expliciete toestemming hebt om te testen. Dit gaat niet over het omzeilen van verdedigingen van iemand anders, maar over hoeveel van het endpoint-oppervlak van een site een bepaald commando echt in kaart brengt.
De drie modi, en wat elke modus kan zien
Standaardmodus is een Go HTTP-client. Die haalt content op, parseert HTML, volgt hrefs en start nooit een browser. Snel, goedkoop, maar blind voor alles wat pas na JavaScript-executie bestaat.
-jc (-js-crawl) koppelt een JavaScript-parser aan die browserloze route. De tool downloadt gelinkte .js-bestanden en haalt string-literals die op URLs lijken uit de broncode. Geen uitvoering, alleen lezen. Er is ook -jsl (jsluice), in de README omschreven als een zwaardere parser met hoger geheugengebruik — die heb ik niet getest, dus ik kan niets zeggen over de impact daarvan op de dekking.

-headless stuurt Chromium aan en voert de scripts van de pagina uit. In deze test was dat de enige Katana-modus die het pad terugvond dat uit fragmenten was opgebouwd en pas tijdens runtime in de DOM werd geplaatst. Dat zegt niet dat elke parser of toekomstige Katana-modus hetzelfde zou vinden.
Dan is er nog het scope-model, en dat is het deel dat ik zou onthouden vóór ik iets in productie typ.
| Flag | Wat het regelt | Waarden / standaard |
|---|---|---|
-fs (field scope) | welke hosts meedoen | dn, rdn, fqdn, of een eigen regex — standaard rdn |
-cs en -cos | URL-regexen die binnen die field scope filteren | — |
-kf | known files: robots.txt en sitemap.xml | de README zegt dat minimaal diepte 3 nodig is |
-d | diepte | standaard 3 |
-resume | gaat verder met een onderbroken crawl | — |
De volgorde is niet cosmetisch: die bepaalt of een host-regex een crawl verruimt of stilletjes leegmaakt.
Setup: één binary, één sterretje
Drie manieren om te installeren, en maar één daarvan heeft een toolchain nodig:
| Installatieroute | Voorwaarde |
|---|---|
Vanuit bron: go install github.com/projectdiscovery/katana/cmd/katana@latest | Go 1.25 of nieuwer is de genoemde vereiste |
| Voorgecompileerde binaries op de releasepagina | geen toolchain |
| Docker-image | geen toolchain |
Bij mij kwam het terecht in ~/go/bin/katana en meldde het bij elke run Current version: v1.6.1. Tot zover het prettige Go-verhaal: één bestand, geen runtime-opstartgedoe.
Het sterretje zit bij headless, waar de browser een extra vereiste is naast de binary:
Waar -headless draait | Wat nodig is |
|---|---|
| Mijn machine | katana detecteerde automatisch een al geïnstalleerde Chromium; ik noteerde de browserbuild niet en gaf geen browserpad op |
| Een kale server, volgens de Ubuntu-instructies van het project | eerst apt install google-chrome-stable voordat headless iets doet |
| Via Docker | draait headless met -system-chrome |
Op een kale server verdwijnt dat gemak direct. Reken dus op een browser, niet alleen op een binary, zodra -headless in je commando staat.
Nog iets kleins dat handig is als je dit in CI draait: katana doet bij het starten een versiecheck naar GitHub. -duc schakelt die uit. Op een laptop is dat vooral ruis; op een air-gapped of rate-limited runner is het een netwerk-roundtrip per run waar je niet om hebt gevraagd. Mijn timing-runs gebruiken -duc, zodat de cijfers echt crawling meten en niet het “even naar huis bellen”.
Hoe ik het testte
Drie endpointcategorieën, bewust gekozen omdat ze de modi van elkaar scheiden. Alles draait op een lokale fixture-server, en de ground truth lag vast vóór er ook maar één crawl begon. Daardoor meet ik recall tegen een vaste set en niet tegen wat katana toevallig uitspuugt.
- Categorie A — gewone HTML.
/page/a,/page/b,/page/c, plus een keten van drie hops/depth/1 → /depth/2 → /depth/3. Elke crawler zou deze moeten vinden. - Categorie B — JavaScript-bestands-literals.
/api/js-endpoint-7en/api/js-endpoint-8bestaan alleen als string-literals in een gelinkt/static/app.js. Leesbaar zonder browser, als iets de JS tenminste leest. - Categorie C — alleen runtime-DOM. Eén pad wordt tijdens runtime opgebouwd uit fragmenten (
'endpoint' + (6 * 7)) en via script in de DOM gezet. De string/runtime-only/endpoint42komt nergens aaneengesloten voor in wat de server verstuurt — niet in de HTML en niet in de JS-broncode. Alleen uitvoering onthult die.
Daarnaast waren er een robots.txt, een sitemap.xml met twee <loc>-endpoints die nergens anders voorkomen, een route die 500 teruggeeft, een dode link en een out-of-scope link naar een tweede server op een andere hostname.
Het meetinstrument is hier net zo belangrijk als de fixture: de server telt wat er daadwerkelijk is opgehaald, dus scope- en resume-claims zijn gebaseerd op echte hits en niet op katana’s stdout. De ruwe runs staan in de benchmark-repo als je de berekening wilt controleren.
De dekking-splitsing die niemand kwantificeert

De matrix, modus per endpointcategorie, bij -d 4:
| Modus | HTML-links (A) | Depth-keten (A) | JS-bestands-literals (B) | Runtime-DOM (C) |
|---|---|---|---|---|
| standard | 4/4 | 3/3 | 0/2 | niet gevonden |
standard -jc | 4/4 | 3/3 | 2/2 | niet gevonden |
-headless | 4/4 | 3/3 | 0/2 | gevonden |
-headless -jc | 4/4 | 3/3 | 0/2 | gevonden |
Lees de laatste twee kolommen samen en het probleem springt eruit. Categorie B werd gevonden door precies één configuratie: standaardmodus met -jc. Categorie C werd gevonden door precies twee configuraties: beide headless-runs. Er is geen enkele rij die in beide kolommen scoort. De volledige matrix staat in discovery-summary.json, waar het berekende veld headless_jc_covers_both false is.
De praktische consequentie is dat “gewoon headless gebruiken voor betere dekking” hier niet compleet was. Headless voegde categorie B niet toe boven op het standaardresultaat; het vond categorie C, maar miste categorie B. Voor volledige dekking van alle geplande categorieën in deze fixture waren twee crawls en een merge nodig:
katana -u https://target.example -jc -d 4 -silent -o pass-jc.txt
katana -u https://target.example -headless -d 4 -silent -o pass-headless.txt
sort -u pass-jc.txt pass-headless.txt > endpoints.txt
De rij -headless -jc is degene waar ik het liefst een upstream-antwoord op zou zien. Het toevoegen van de JavaScript-parser aan de headless-run leverde niets extra’s op — nog steeds 0/2 voor categorie B, in elke run, ook na een frisse reproductie. Ik rapporteer hier gedrag, geen verklaring; ik heb katana’s interne werking niet geïnstrumenteerd om uit te zoeken waarom het browserpad geen JS-bestands-literals bijdraagt. Zie het als een reproduceerbare observatie en een goede GitHub-issue, niet als diagnose. Overigens: de combinatie -hl -jc eindigde op v1.6.1 op macOS ARM netjes met returncode 0, wat historisch niet altijd zo is geweest.
De officiële docs beschrijven headless als beter qua dekking, en dat klopte hier voor de runtime-gerenderde categorie. De gecontroleerde richtlijn maakte dit source-literal/runtime-DOM-verschil niet expliciet, dus zie deze matrix als een reden om beide paden op je eigen endpointcategorieën te testen, niet als een universele taxonomie.
Wat headless kost aan wall time
Drie opeenvolgende runs per modus op een verder idle machine:
| Modus | p50 | min–max | mean |
|---|---|---|---|
| standard | 13.08s | 13.07–13.17s | 13.11s |
-headless | 66.82s | 66.78–67.68s | 67.09s |
Dat is een verhouding van 5,1x, met bereiken die nauwelijks in de buurt komen van overlap — mijn traagste standaardrun (13.17s) was nog altijd meer dan 53 seconden sneller dan mijn snelste headless-run (66.78s) (cost-summary.json). Dit is geen meetruis.
Eén kanttekening bij die 13 seconden: mijn fixture bevat bewust een 500-route en een dode link, en standaardmodus wacht op beide de standaard -timeout 10 retry-tail uit. Ik heb de timeout niet zo afgesteld dat de snelle modus mooier lijkt; een getunede standaardrun zou de kloof waarschijnlijk juist groter maken in plaats van kleiner.
De verhouding is een lokale capaciteitsindicatie, geen productievoorspelling. Echte targets verschillen in latency, fouten, scriptwerk en scheduling, terwijl deze fixture een default timeout-tail bevat. Gebruik de gemeten 5,1x om te bepalen of headless een apart budget en een aparte subset targets verdient, en benchmark dat plan vervolgens op representatieve geautoriseerde hosts.
Scope bleef staan, maar één flag deed stilletjes niets
De scope-test gebruikte twee servers: de primaire op 127.0.0.1, en een tweede die als localhost op een andere poort bereikbaar was en een pad serveerde dat alleen daar bestond. Een hit op dat pad bewijst dus dat de out-of-scope host daadwerkelijk is opgehaald, en niet alleen geprint.
| Configuratie | Out-of-scope host opgehaald? | Hits op de tweede server |
|---|---|---|
standaard (-fs rdn) | nee | 0 |
-fs fqdn | nee | 0 |
-cs localhost | nee | 0 |
| `-fs '(127.0.0.1 | localhost)'` | ja |
Scope-discipline is goed nieuws: standaard bleef katana netjes thuis, en er was een expliciete actie nodig om uit te breiden. Dat is het juiste default-gedrag voor een tool die op andermans infrastructuur wordt losgelaten.
De interessante rij is -cs localhost. Die breidde de crawl niet uit naar de tweede host — en leverde ook helemaal geen URLs op. Omdat -cs binnen de field scope filtert, en die field scope nog steeds de primaire host was, matchte de regex niets en gaf de crawl een lege set terug in plaats van een fout. Als je ooit een crawl-scope-regex hebt geschreven met de host die je wilde meenemen, en vervolgens naar een leeg outputbestand zat te staren, dan is dát het mechanisme (scope-summary.json). Wil je een host toevoegen, stel dan -fs in. Wil je binnen bestaande hosts filteren, gebruik dan -cs/-cos.
Resume is grover dan de flag doet vermoeden
De README beschrijft de flag als -resume string resume scan using resume.cfg, wat klinkt alsof er een bestand in je werkdirectory verschijnt. Maar dat is niet zo. Op mijn machine werd het checkpoint weggeschreven naar ~/.config/katana/resume-<xid>.cfg — gemeten, niet uit de docs gehaald, omdat de docs geen pad noemen.
Wat erin zit is het belangrijkere verrassingseffect. Het bestand bevatte een InFlightUrls-map met precies één ding: de seed-URL. Niet de bezochte set, niet de frontier. Dit gebeurde er dus toen ik een crawl na drie seconden met SIGINT onderbrak en daarna hervatte:
| Run | Aantal unieke paden |
|---|---|
| Volledige baseline crawl | 11 |
| Opgehaald vóór de onderbreking | 10 |
| Opnieuw opgehaald door de resume-run | alle 11, inclusief alle 10 die al klaar waren |
Resume kwam wel op exact dezelfde eindset aan endpoints uit, dus er is niets stuk. Maar de checkpoint-granulariteit is per input-seed, niet per URL — de in-memory dedup-filter wordt nooit gepersisteerd, dus een hervatte single-seed crawl begint die seed opnieuw vanaf nul (resume-summary.json). Als je katana een lijst van 500 hosts geeft, zou resume je dus de hosts moeten besparen die volledig klaar waren; dat multi-seed-gedrag volgt uit de manier waarop de state is opgeslagen, maar ik heb alleen de single-seed-case gemeten. Als je diep in één enorme site zit, koopt resume je dus correctheid, niet tijd.
Known files: wel opgevraagd, daarna laten vallen

-kf all -d 3 vroeg beide bestanden inderdaad op — robots.txt en sitemap.xml verschenen in het hitlog van de server — en vond vervolgens 0 van 2 endpoints uit de <loc>-elementen in die sitemap. Recall 0.0.
Voor ik dat als beperking bestempelde, probeerde ik het eerst op mijn eigen manier fout te maken. Elke variatie leverde hetzelfde op:
| Geteste variatie | Endpoints uit sitemap-<loc> hersteld |
|---|---|
-kf all | 0/2, recall 0.0 |
-kf sitemapxml | 0/2, recall 0.0 |
-kf robotstxt | 0/2, recall 0.0 |
| diepte 3 | 0/2, recall 0.0 |
| diepte 4 | 0/2, recall 0.0 |
| diepte 5 | 0/2, recall 0.0 |
met -jc erbij | 0/2, recall 0.0 |
direct gestart op /sitemap.xml | 0/2, recall 0.0 |
De gedocumenteerde vereiste — gebruik -kf, ga minstens drie niveaus diep — was iedere keer voldaan. Dit is dus geen verhaal over een ontbrekende flag.
De nuttige conclusie komt eerst: op deze IP-literal fixture moet je er niet van uitgaan dat het opvragen van known files automatisch betekent dat de <loc>-URLs ook echt zijn meegecrawld. Controleer recall, of extraheer en seed die URLs zelf.
De codepath in v1.6.1 past bij deze observatie, maar ik heb hem tijdens de run niet geïnstrumenteerd. In sitemapxml.go in v1.6.1 bouwt NewNavigationRequestURLFromResponse <loc>-navigatieverzoeken op zonder een gevulde RootHostname. Het verzoek komt daarna bij ValidateScope terecht; in scope.go in v1.6.1 vergelijkt de IP-literal branch de URL-host met die lege root en kan die worden afgewezen. Het custom -fs '(127.0.0.1|localhost)'-commando nam in een aparte scope-test een andere scope-branch, dus dat is een door de bron voorspelde redding, niet een gemeten workaround voor -kf. Een bevestigingspoging liep vast op incidentele dialingproblemen in de known-files-client op deze host; het gerapporteerde resultaat blijft daarom 0/2.
Wat ik in productie echt zou doen, totdat iemand de flag bevestigt: haal de sitemap zelf op, extraheer de <loc>-URLs en geef die als seed-lijst aan katana. Twee shellregels, zonder scope-validatie ertussen.
Eén ding werkte precies zoals geadverteerd en verdient daarom een zin: de 500-route en de dode link werden opgehaald, gelogd en daarna gewoon overgeslagen. Elke browserloze run eindigde met returncode 0. Een crawler die op de eerste foutmelding sterft is onbruikbaar zonder toezicht, en katana doet dat dus niet.
Een targetspecifieke coverage-check vóór deployment
De fixture-matrix is het nuttigst als sjabloon om je eigen geautoriseerde targets te testen. Definieer endpointcategorieën vóór je Katana draait: gewone links, literals in gelinkte scripts, routes die pas na uitvoering ontstaan, en known-file-entries zijn vier logische startgroepen. Houd per categorie een kleine ground-truthsample aan. Zonder zo’n lijst vooraf kan een grotere stdout-output eruitzien als betere dekking, ook als een categorie stilletjes verdwenen is.
Meet browserloze en headless-paden eerst als aparte metingen. Sla de exacte commando’s, Katana-versie, browserbuild, returncodes en outputs op. Normaliseer en diff de endpointsets in plaats van alleen regels te tellen. Als standaard -jc op jouw sample niets unieks toevoegt, kan headless-only voldoende zijn; als de sets uiteenlopen zoals hier, houd de twee passes dan apart en merge ze na het verzamelen. Voeg niet zomaar beide flags toe aan één commando en ga er niet van uit dat dat meteen gelijkstaat aan de unie, totdat een targetspecifieke diff dat bewijst.
Valideer scope met bewijs buiten katana’s output om. Zet een canary-URL op een host die uitgesloten zou moeten blijven en inspecteer het requestlog van die server. Test ook een beoogde tweede host als de crawl juist moet verruimen. De -cs localhost-run hier leverde een lege output op, omdat content-scope filtering de field scope niet uitbreidde; de custom -fs '(127.0.0.1|localhost)'-aanroep raakte juist wel de tweede server. Het opslaan van de exacte regex is belangrijk, omdat een wijziging van één teken de regex zelf kan veranderen, niet alleen de presentatie ervan.
Test onderbreking en known files los van discovery recall. Onderbreek voor resume een representatieve seed na enkele pagina’s, sla het gegenereerde checkpoint-pad op en tel hoeveel afgeronde URLs opnieuw worden opgehaald. Controleer voor -kf zowel of robots/sitemap werden opgevraagd als of geplande <loc>-URLs echt zijn ingepland. Dat zijn verschillende beweringen. In deze fixture werden de bestanden opgehaald terwijl de twee sitemap-endpoints afwezig bleven, dus zowel requestlogs als endpoint-output waren nodig om de grens te zien.
Bepaal ten slotte een lokale kostenbaseline met opeenvolgende runs op een verder idle machine en herhaal dat op representatieve hosts. Bewaar min, max en median in plaats van alleen een vermenigvuldigingsfactor. De 5,1x hier omvat het fout- en timeoutgedrag van deze fixture; het vertelt je dat headless een eigen budget verdient, niet hoe lang een productie-inventarisatie zal duren.
Voor- en nadelen
Voordelen:
- Perfecte recall op gewone HTML in elke modus — 4/4 links en de volledige 3/3 depth-keten, zonder configuratie.
-jcwerkt echt browserloos: 2/2 endpoints hersteld uit string-literals in een gelinkt JS-bestand, zonder browserkosten.-headlessis de enige modus die een endpoint vond dat tijdens runtime was opgebouwd — een categorie die per definitie onzichtbaar is voor broncode-parsewerk.- De scope-defaults zijn conservatief. De out-of-scope host werd nooit opgehaald onder default,
-fs fqdnof-cs. - Eén Go-binary, MIT-licentie, voorgecompileerde builds en een Docker-image, met pipeline-vormige I/O.
- Robuust bij fouten: 500’s en dode links stoppen de crawl niet.
Nadelen:
- Geen enkele run ving zowel JS-bestands- als runtime-DOM-endpoints af. Volledige dekking vraagt twee runs en een merge.
-jcvoegde onder-headlessniets toe — 0/2 voor categorie B in elke headless-run.- Headless kost 5,1x wall time (p50 66.82s versus 13.08s, met niet-overlappende bereiken).
-resumecrawlt afgeronde pagina’s binnen één seed opnieuw. Het herstelt de endpointset, niet je verstreken tijd.- Known files vroegen robots.txt en sitemap.xml op, maar haalden 0/2 sitemap-
<loc>-endpoints terug tegen een IP-target. - Headless vereist stiekem een Chromium op de machine; het “één binary”-verhaal stopt bij de browser.
- Alleen discovery. Geen gestructureerde extractie, geen contentconversie, geen veldschema.
Niet getest, en dus buiten wat deze cijfers afdekken: -jsluice, een specifieke depth-cutoff-test met -d 1/-d 2, multi-seed resume, automatisch formulieren invullen, en echte JavaScript-zware of beschermde productie-sites. Alle cijfers komen van één machine (macOS arm64) tegen een lokale fixture.
Voor wie het is, en wie beter kan overslaan
Als jouw werk bestaat uit het maken van een endpoint-inventaris van infrastructuur die je mag benaderen, dan heeft katana precies de juiste pipeline-vorm: STDIN/STDOUT, een deelbare binary, en zowel browserloze als browser-gestuurde modi. De two-pass merge was nodig voor de geplande categorieën in deze fixture; of jouw targets beide passes nodig hebben, moet je met representatieve pagina’s vaststellen.
Sla het over als je data wilt in plaats van adressen. Katana geeft je nooit een tabel met producten; het geeft je de URLs waar producten mogelijk staan, en iets anders doet de extractie. Sla het ook over als je één commando nodig hebt dat compleet is — de two-pass merge is prima in een pipeline, maar irritant aan een prompt. En als je enumeratie leunt op sitemap-<loc>-endpoints terwijl je IP’s target, verifieer dan eerst wat je werkelijk krijgt voordat je de output vertrouwt, want in mijn fixture leverde dat pad niets op.
Alternatieven, en de extractiegrens
Katana is gratis, MIT-gelicenseerd en self-hosted. Discovery, moduskeuze, browser-implementatie en resultaat-merging blijven aan jouw kant van de grens.
Binnen open source zijn de nuttige vergelijkingen functiegericht, niet taalgericht. Colly is de andere Go-optie, maar het is een library die je met je eigen callbacks compileert en die helemaal geen JavaScript rendert. Crawl4AI draait een echte browser en levert Markdown voor LLM-pipelines, wat een heel ander outputformaat is. Als je meerdere van deze opties naast elkaar afweegt, zet onze open-source scraper roundup de categorieën overzichtelijk naast elkaar.
Disclosure: Thunderbit is het product van de uitgever en is in deze Katana-fixture niet getest. Het zit stroomafwaarts in de beheerde-extractiecategorie en zet pagina’s om naar tekst of gestructureerde records in plaats van het endpoint-oppervlak van een geautoriseerde target te inventariseren. Een workflow kan beide categorieën gebruiken, maar deze review levert alleen bewijs over Katana’s discovery-gedrag.
Probeer Thunderbit voor webdata-extractie
Oordeel
Gebruik katana wanneer het eindresultaat een endpointlijst is voor targets die je mag crawlen en wanneer je mode-dekking tegen die targets kunt valideren. In deze fixture vond standaard -jc de geplande JavaScript-bestands-literals terug, terwijl headless het runtime-DOM-endpoint vond; de geteste browserloze modi van Katana vonden dat runtime-pad niet terug. De standaard scope liet bovendien de tweede host met rust, en browserloze runs gingen gewoon door na de 500 en de dode link.
De kanttekeningen zijn operationeel: voor gemengde endpointcategorieën kan een two-pass merge nodig zijn, headless kostte ongeveer vijf keer de lokale wall time, single-seed resume haalde afgeronde paden opnieuw op, en known-files scoorde 0/2 tegen de IP-target. Dat zijn resultaten van v1.6.1 in deze fixture, geen garanties voor elke site. Ze zijn wel genoeg om de checks te definiëren die een productie-evaluatie opnieuw moet uitvoeren.
Probeer Thunderbit voor webdata-extractie Get Started Free
Veelgestelde vragen
Waar slaat katana het resume-bestand op, en slaat resuming al gecrawlde pagina’s over?
Het checkpoint belandde in ~/.config/katana/resume-<xid>.cfg, niet in een resume.cfg in de werkdirectory zoals de helptekst van de flag suggereert. En nee, completed pages worden niet overgeslagen: het bestand bewaart alleen in-flight seed-URLs, dus een hervatte single-seed crawl haalde alle 11 baseline-paden opnieuw op, inclusief de 10 die al klaar waren. Je krijgt dus dezelfde uiteindelijke endpointset, maar niet de bespaarde tijd.
Waarom vroeg -kf all mijn sitemap.xml wel op, maar crawlde het de URLs erin niet?
Tegen een IP-target past dit resultaat bij een scope-validatiegrens in plaats van een fout in de flag. In de code van v1.6.1 bouwt Katana’s sitemap-parser elke <loc>-request op zonder de root hostname door te geven, en de DNS-scopecheck voor IP-literal hosts kan die URL vervolgens afwijzen; ik heb de run niet geïnstrumenteerd om dat mechanisme te bevestigen. Het bleef op 0 recall in elke combinatie van flag, diepte en seed die ik probeerde. Een eigen -fs-hostregex neemt een andere validatiebranch en is de door de bron voorspelde oplossing — maar ik kon dat op mijn machine niet bevestigen met -kf, dus zie het als ongetest. De <loc>-URLs zelf extraheren en ze vervolgens als seeds aan katana meegeven is de aanpak die ik vandaag zou vertrouwen.
Wat moet ik bewaren wanneer ik een Katana-coverage-test rapporteer?
Noteer de exacte Katana-versie en het commando, inclusief de byte-exacte -fs-expressie; definieer endpointcategorieën vóór de run; bewaar server-side hitlogs naast stdout; en houd gemeten gedrag apart van hypotheses op basis van de broncode. Voor headless-runs moet je ook de browserbuild vastleggen — die ontbrak in deze test, wat de reproduceerbaarheid beperkt.
Moet ik -jc, -headless, of beide gebruiken?
Kies op basis van de endpointcategorieën die je nodig hebt. In deze fixture vond standaard -jc de literals in een JavaScript-bestand, terwijl headless het endpoint vond dat in de runtime-DOM werd ingevoegd. Geen van beide modi dekte alleen beide categorieën, dus een two-pass-run met deduplicatie was hier de verdedigbare keuze voor gemengde targets.
Stopt één mislukte URL de crawl? Niet in deze gecontroleerde run. Katana ging door na zowel een 500-response als een dode link en gaf nog steeds de andere bereikbare paden terug. Dat is geen vervanging voor foutafhandeling in productie: houd logs van mislukte requests bij en definieer een acceptabel foutpercentage, zodat een gedeeltelijk geslaagde crawl niet als volledige dekking wordt aangezien.


