Katana é o crawler de descoberta de endpoints do ProjectDiscovery — um binário em Go, com licença MIT, que recebe um alvo e devolve URLs e endpoints para a próxima ferramenta do pipeline. Ele rastreia em modo HTTP sem navegador ou com -headless, que aciona o Chromium. A documentação oficial apresenta o modo headless como a opção com maior cobertura; este teste mostra por que o tipo de endpoint importa tanto quanto a quantidade.
Eu montei um site pequeno com três classes de endpoints propositalmente diferentes e medi, na v1.6.1 com -d 4, qual modo encontrava cada uma. O HTML comum alcançou 4/4 links e toda a cadeia de três saltos em todas as quatro configurações. A diferença apareceu nos endpoints expostos via código JavaScript versus mudanças no DOM em tempo de execução.
Neste teste, o headless encontrou a classe de runtime DOM que os modos sem navegador deixaram passar, enquanto o modo padrão com -jc encontrou literais em arquivos JavaScript que as duas execuções headless não viram. Nenhuma linha da matriz com os quatro comandos cobriu as duas classes ao mesmo tempo. Escopo, retomada e comportamento de known files definiram os outros limites práticos.
O que o katana realmente é
O crawler katana — projectdiscovery/katana no GitHub — é escrito em Go e tem licença MIT. Testei a versão v1.6.1 em 27 de julho de 2026; a versão importa porque os resultados de cobertura e de known files abaixo são observações específicas dessa build.
Aqui, o rótulo da categoria importa mais do que o normal. Um crawler de descoberta de endpoints não é uma ferramenta de extração de campos. Se você quer um crawler Go que entregue nomes de produtos e preços como JSON estruturado, o katana está no corredor errado — ele vai dizer com alegria que /products/1138 existe e não vai dizer absolutamente nada sobre o conteúdo dessa página. Isso é intencional, e julgá-lo pela extração seria como avaliar um detector de metais pela capacidade de estimar o valor de joias.
O terreno dele é recon e automação para segurança ofensiva: entra STDIN, saem URLs; você encadeia a saída na próxima ferramenta. Daí vem o aviso óbvio — todas as medições aqui foram feitas em um fixture em 127.0.0.1 que eu mesmo escrevi. Use katana apenas em hosts que você possui ou para os quais tem autorização formal de teste, e em nenhum outro lugar. Nada disso é sobre burlar defesas de terceiros; é sobre quantificar quanto da superfície de endpoints de um site determinado comando realmente enumera.
Os três modos e o que cada um vê
O modo padrão é um cliente HTTP em Go. Ele busca, faz o parse do HTML, segue hrefs e nunca inicia um navegador. Rápido, barato, mas cego para tudo que só existe depois da execução de JavaScript.
-jc (-js-crawl) adiciona um parser de JavaScript ao caminho sem navegador. Ele baixa arquivos .js vinculados e extrai literais de string com cara de URL do código-fonte. Sem execução, apenas leitura. Existe também -jsl (jsluice), descrito no README como um parser mais pesado e mais exigente em memória — eu não o testei, então não tenho nada a dizer sobre ele alterar ou não o panorama de cobertura.

-headless controla o Chromium e executa os scripts da página. Neste fixture, foi o único modo testado do Katana que recuperou o caminho montado a partir de fragmentos e inserido no DOM em tempo de execução. Esse resultado não prova o que qualquer parser ou futuro modo do Katana possa recuperar.
Depois vem o modelo de escopo, que é a parte que eu internalizaria antes de digitar qualquer coisa em produção.
| Flag | O que controla | Valores / padrão |
|---|---|---|
-fs (field scope) | quais hosts entram no jogo | dn, rdn, fqdn, ou uma regex personalizada — padrão rdn |
-cs e -cos | regex de URL que filtram dentro desse escopo de campo | — |
-kf | known files: robots.txt e sitemap.xml | o README diz que precisa de profundidade mínima 3 |
-d | profundidade | padrão 3 |
-resume | retoma um crawl interrompido | — |
A ordem não é decorativa: é ela que decide se uma regex de host amplia o crawl ou o esvazia silenciosamente.
Configuração: um binário, um asterisco
Três caminhos de instalação, e só um deles pede toolchain:
| Forma de instalação | Pré-requisito |
|---|---|
A partir do código-fonte: go install github.com/projectdiscovery/katana/cmd/katana@latest | Go 1.25 ou superior é o requisito declarado |
| Binários pré-compilados na página de releases | sem toolchain |
| Imagem Docker | sem toolchain |
O meu ficou em ~/go/bin/katana e mostrou Current version: v1.6.1 em todas as execuções. Até aqui, a história clássica do Go: um arquivo, sem runtime.
O asterisco é o headless, em que o navegador é um requisito separado do binário:
Onde o -headless roda | O que precisa |
|---|---|
| Na minha máquina | o katana detectou automaticamente um Chromium já instalado; não registrei a versão do navegador nem forneci um caminho para ele |
| Em um servidor básico, segundo as instruções de Ubuntu do projeto | apt install google-chrome-stable antes de o headless fazer qualquer coisa |
| Pelo caminho Docker | executa headless com -system-chrome |
Em um servidor básico, essa conveniência desaparece. Planeje um navegador, não só um binário, no momento em que -headless entrar na linha de comando.
Um detalhe menor, mas útil se você rodar isso em CI: o katana faz uma checagem de versão no GitHub ao iniciar. -duc desabilita isso. Em um notebook isso é ruído; em um runner sem acesso à rede ou com limite de taxa, é uma ida e volta de rede por execução que você não pediu. Meus testes de tempo usam -duc para que os números meçam o crawling, e não um telefonema para casa.
Como eu testei
Três classes de endpoint, escolhidas justamente porque separam os modos. Tudo vive em um servidor local de fixture, e a verdade-base foi anotada antes de qualquer crawl, de modo que o recall é medido contra um conjunto fixo, não contra o que o katana resolveu imprimir.
- Classe A — HTML simples.
/page/a,/page/b,/page/c, além de uma cadeia de três saltos/depth/1 → /depth/2 → /depth/3. Qualquer crawler deveria pegar isso. - Classe B — literais em arquivo JavaScript.
/api/js-endpoint-7e/api/js-endpoint-8existem apenas como literais de string dentro de um/static/app.jsvinculado. Dá para ler sem navegador, se alguém se der ao trabalho de ler o JS. - Classe C — apenas runtime DOM. Um caminho montado em tempo de execução a partir de fragmentos (
'endpoint' + (6 * 7)) e inserido no DOM pelo script. A string/runtime-only/endpoint42nunca aparece contígua em nenhum byte enviado pelo servidor — nem no HTML, nem no código-fonte JS. Só a execução revela isso.
Além disso, havia um robots.txt, um sitemap.xml com dois endpoints <loc> que não aparecem em nenhum outro lugar, uma rota que devolve 500, um link quebrado e um link fora do escopo apontando para um segundo servidor em outro hostname.
O instrumento importa tanto quanto o fixture: o servidor conta o que de fato foi buscado, então as alegações de escopo e resume se apoiam em verdade de requisição, não na própria saída do katana. As execuções brutas estão comitadas no repositório de benchmark se você quiser conferir minha conta.
A divisão de cobertura que ninguém quantifica

A matriz, modo por classe de endpoint, em -d 4:
| Modo | Links HTML (A) | Cadeia de profundidade (A) | Literais em JS (B) | Runtime DOM (C) |
|---|---|---|---|---|
| standard | 4/4 | 3/3 | 0/2 | não encontrado |
standard -jc | 4/4 | 3/3 | 2/2 | não encontrado |
-headless | 4/4 | 3/3 | 0/2 | encontrado |
-headless -jc | 4/4 | 3/3 | 0/2 | encontrado |
Leia as duas últimas colunas em conjunto e o problema salta aos olhos. A classe B foi encontrada por exatamente uma configuração: o modo padrão com -jc. A classe C foi encontrada por exatamente duas: as duas execuções headless. Não existe nenhuma linha com acerto nas duas colunas. A matriz completa está em discovery-summary.json, onde o campo calculado headless_jc_covers_both aparece como false.
A consequência prática é que “basta usar headless para ter melhor cobertura” estava incompleto para este teste. O headless não acrescentou a classe B sobre o resultado padrão; ele recuperou a classe C enquanto deixava a classe B passar. Cobrir todas as classes plantadas neste fixture exigiu dois crawls e um merge:
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
A linha -headless -jc é a que eu mais gostaria de ver respondida pelo upstream. Adicionar o parser JavaScript à execução headless não trouxe nada de novo — continuou em 0/2 na classe B, em todas as execuções, inclusive numa reprodução fresca. Estou reportando o comportamento, não dizendo que encontrei o mecanismo; eu não instrumentei o interior do katana para descobrir por que o caminho do navegador deixa de contribuir com literais de arquivos JS. Trate isso como uma observação reproduzível e um bom issue no GitHub, não como diagnóstico. (Vale notar junto: a combinação -hl -jc concluiu normalmente com código de retorno 0 na v1.6.1 em macOS ARM, o que historicamente nem sempre foi verdade.)
A documentação oficial descreve o headless como tendo melhor cobertura, e isso de fato ocorreu para a classe renderizada em runtime neste teste. A orientação revisada não explicitava essa divisão entre literal de fonte e runtime DOM, então trate a matriz como motivo para testar ambos os caminhos em suas próprias classes de endpoint, e não como uma taxonomia universal.
Quanto o headless custa em tempo total
Três execuções sequenciais por modo, em uma máquina ociosa:
| Modo | p50 | min–máx | média |
|---|---|---|---|
| standard | 13.08s | 13.07–13.17s | 13.11s |
-headless | 66.82s | 66.78–67.68s | 67.09s |
Isso dá uma razão de 5.1x, com intervalos que nem chegam perto de se sobrepor — minha execução padrão mais lenta (13.17s) ainda foi mais de 53 segundos mais rápida do que minha execução headless mais rápida (66.78s) (cost-summary.json). Isso não é ruído de medição.
Uma observação sobre esses 13 segundos: meu fixture inclui de propósito uma rota 500 e um link quebrado, e o modo padrão aguenta a cauda de retry do -timeout 10 padrão em ambos. Eu não ajustei o timeout para favorecer o modo rápido, o que significa que um run padrão tunado provavelmente aumentaria a diferença em vez de reduzi-la.
A razão é um sinal de capacidade local, não uma previsão de produção. Alvos reais variam em latência, falhas, trabalho de script e agendamento, enquanto este fixture inclui uma cauda de timeout padrão. Use a diferença medida de 5.1x para decidir se o headless merece orçamento e subconjunto de alvos próprios, e então valide esse plano em hosts autorizados representativos.
O escopo se manteve, mas uma flag não fez nada em silêncio
O teste de escopo usou dois servidores: o principal em 127.0.0.1 e um segundo acessível como localhost em outra porta, servindo um caminho que existe apenas ali. Portanto, um hit nesse caminho prova que o host fora do escopo foi realmente buscado, e não apenas impresso.
| Configuração | Host fora do escopo buscado? | Hits no segundo servidor |
|---|---|---|
padrão (-fs rdn) | não | 0 |
-fs fqdn | não | 0 |
-cs localhost | não | 0 |
| `-fs '(127.0.0.1 | localhost)'` | sim |
A disciplina de escopo é uma boa notícia: por padrão, o katana ficou no próprio território, e foi necessária uma ação explícita para ampliar. Esse é o comportamento certo para uma ferramenta apontada para infraestrutura de terceiros.
A linha interessante é -cs localhost. Ela não ampliou o crawl para o segundo host — e também emitiu zero URLs. Como -cs filtra dentro do field scope, e o field scope continuava sendo o host primário, a regex não encontrou nada e o crawl retornou um conjunto vazio em vez de um erro. Se você já escreveu uma regex de escopo de crawl nomeando um host que queria incluir e encarou um arquivo de saída vazio, esse é o mecanismo (scope-summary.json). Para adicionar um host, use -fs. Para restringir dentro dos hosts que você já tem, use -cs/-cos.
Resume é mais grosso do que a flag sugere
O README descreve a flag como -resume string resume scan using resume.cfg, o que parece um arquivo largado no diretório de trabalho. Não é. Na minha máquina, o checkpoint foi gravado em ~/.config/katana/resume-<xid>.cfg — medido, não lido numa página de docs, porque a documentação não informa o caminho.
O conteúdo é a surpresa mais importante. O arquivo tinha um mapa InFlightUrls contendo exatamente uma coisa: a URL semente. Não o conjunto visitado, nem a fronteira. Então foi isso que aconteceu quando interrompi um crawl com SIGINT depois de três segundos e o retomei:
| Execução | Caminhos distintos |
|---|---|
| Crawl completo de referência | 11 |
| Buscados antes da interrupção | 10 |
| Buscados novamente pela execução de resume | todos os 11, inclusive os 10 que já tinham sido concluídos |
O resume chegou ao mesmo conjunto final de endpoints, então nada está quebrado. Mas a granularidade do checkpoint é por seed de entrada, não por URL — o filtro de deduplicação em memória nunca é persistido, então retomar um crawl de uma única seed faz com que essa seed seja recrawleada do zero (resume-summary.json). Se você alimentar o katana com uma lista de 500 hosts, o resume deve poupar os hosts que terminaram completamente; esse comportamento com múltiplas seeds decorre de como o estado é armazenado, mas eu só medi o caso de uma única seed. Se você estiver profundamente dentro de um site gigantesco, resume compra correção, não tempo.
Known files: solicitado, depois ignorado

-kf all -d 3 de fato solicitou os dois arquivos — robots.txt e sitemap.xml apareceram no log de hits do servidor — e depois recuperou 0 de 2 dos endpoints listados nos elementos <loc> desse sitemap. Recall 0.0.
Antes de chamar isso de limitação, tentei fazer parecer culpa minha. Todas as variações recuperaram a mesma coisa:
| Variação testada | Endpoints <loc> do sitemap recuperados |
|---|---|
-kf all | 0/2, recall 0.0 |
-kf sitemapxml | 0/2, recall 0.0 |
-kf robotstxt | 0/2, recall 0.0 |
| profundidade 3 | 0/2, recall 0.0 |
| profundidade 4 | 0/2, recall 0.0 |
| profundidade 5 | 0/2, recall 0.0 |
com -jc adicionado | 0/2, recall 0.0 |
seed direto em /sitemap.xml | 0/2, recall 0.0 |
O requisito documentado — usar -kf e ir pelo menos três níveis fundo — foi atendido todas as vezes. Não se trata de flag ausente.
A decisão útil vem antes: neste fixture com IP literal, não assuma que solicitar known files significa que as URLs <loc> deles entraram no crawl. Verifique o recall ou extraia e alimente essas URLs manualmente.
O caminho de código da v1.6.1 é compatível com a observação, mas eu não o instrumentei durante a execução. Em sitemapxml.go na v1.6.1, NewNavigationRequestURLFromResponse cria requests de navegação a partir de um response sem um RootHostname preenchido. O request então chega a ValidateScope; em scope.go na v1.6.1, o ramo de IP literal compara o host da URL com esse root vazio e pode rejeitá-lo. O comando personalizado -fs '(127.0.0.1|localhost)' seguiu um ramo de escopo diferente em um teste separado, então ele é um resgate previsto pela origem, não um workaround medido para -kf. Uma tentativa de confirmação foi bloqueada por instabilidade de conexão no cliente de known files neste host; por isso, o resultado reportado continua 0/2.
O que eu faria em produção, até alguém confirmar a flag: buscaria o sitemap eu mesmo, extraindo as URLs <loc> e passando-as ao katana como lista de seeds. Duas linhas de shell, sem validação de escopo no caminho.
Uma coisa se comportou exatamente como anunciado e merece uma frase: a rota 500 e o link quebrado foram buscados, registrados e ignorados. Todas as execuções sem navegador terminaram com código de retorno 0. Um crawler que morre no primeiro response ruim é inútil sem supervisão, e o katana não faz isso.
Um check de cobertura específico do alvo antes do deploy
A matriz do fixture é mais útil como modelo para testar seus próprios alvos autorizados. Defina classes de endpoint antes de rodar o Katana: links simples, literais em scripts vinculados, rotas criadas só depois da execução e entradas de known files são quatro baldes iniciais razoáveis. Mantenha uma pequena amostra de verdade-base para cada classe. Sem essa lista prévia, uma saída stdout maior pode parecer mais cobertura mesmo quando uma classe desapareceu.
Execute os caminhos sem navegador e com navegador como medições separadas primeiro. Salve os comandos exatos, a versão do Katana, a build do navegador, os códigos de retorno e as saídas. Normalize e compare os conjuntos de endpoints em vez de comparar contagens de linhas. Se o -jc padrão não agregar nada de único na sua amostra, uma política só headless pode bastar; se os conjuntos divergirem como aconteceu aqui, mantenha as duas passagens separadas e faça o merge depois da coleta. Não assuma que adicionar as duas flags em um único comando equivale à união até que o diff específico do alvo prove isso.
Valide o escopo com evidência fora da saída do Katana. Coloque uma URL canário em um host que deveria ficar excluído e inspecione o log de requisições desse servidor. Também teste um segundo host pretendido, se o crawl deveria se ampliar. A execução -cs localhost aqui produziu saída vazia porque o filtro de content-scope não expandiu o field scope; a invocação personalizada -fs '(127.0.0.1|localhost)' de fato contatou o segundo servidor. Registrar a expressão regular exata importa porque uma mudança de um caractere pode alterar a regex, não apenas sua apresentação.
Teste interrupção e known files separadamente do recall de descoberta. Para resume, interrompa uma seed representativa depois de algumas páginas, salve o caminho do checkpoint gerado e conte quantas URLs já concluídas são buscadas novamente. Para -kf, confirme tanto que robots/sitemap foram solicitados quanto que as URLs <loc> plantadas realmente foram agendadas. Essas são afirmações diferentes. Neste fixture, os arquivos foram buscados enquanto os dois endpoints do sitemap estavam ausentes, então logs de requisição e saída de endpoints foram ambos necessários para enxergar o limite.
Por fim, estabeleça uma linha de base local de custo usando execuções sequenciais em uma máquina ociosa, e depois repita em hosts representativos. Preserve mínimo, máximo e mediana, e não apenas um multiplicador. O fator 5.1x aqui inclui o comportamento de falha e timeout deste fixture; ele diz que o headless merece orçamento próprio, não quanto tempo um inventário de produção vai levar.
Prós e contras
Prós:
- Recall perfeito em HTML comum em todos os modos — 4/4 links e a cadeia completa de 3/3 saltos, sem exigir configuração.
-jcrealmente funciona sem navegador: 2/2 endpoints recuperados de literais em um arquivo JS vinculado, sem custo de browser.-headlessé a única coisa que encontrou um endpoint montado em tempo de execução — uma classe que, por construção, é invisível ao parser de fonte.- Os padrões de escopo são conservadores. O host fora do escopo nunca foi buscado com
default,-fs fqdnou-cs. - Binário Go único, licença MIT, builds pré-compilados e imagem Docker, entrada e saída em formato de pipeline.
- Robusto diante de falhas: 500s e links quebrados não interrompem o crawl.
Contras:
- Nenhuma única invocação cobriu endpoints em arquivo JS e runtime DOM. A cobertura total exige duas execuções e merge.
-jcnão acrescentou nada sob-headless— 0/2 na classe B em todas as execuções headless.- Headless custa 5.1x mais tempo total (p50 de 66.82s vs 13.08s, intervalos não sobrepostos).
-resumerecrawleia páginas já concluídas dentro de uma seed. Ele restaura o conjunto de endpoints, não o tempo gasto.- Known files solicitou robots.txt e sitemap.xml, mas recuperou 0/2 dos endpoints
<loc>do sitemap contra um alvo IP. - Headless exige silenciosamente um Chromium na máquina; a história do “um binário” termina no navegador.
- É só descoberta. Sem extração estruturada, sem conversão de conteúdo, sem schema de campos.
Não testado, e portanto fora do alcance de qualquer número aqui: -jsluice, um teste dedicado de corte de profundidade em -d 1/-d 2, resume com múltiplas seeds, preenchimento automático de formulários e qualquer site de produção real com muito JavaScript ou proteção. Todos os números são de uma máquina (macOS arm64) contra um fixture local.
Para quem é, e quem deve pular
Se o seu trabalho é produzir um inventário de endpoints de uma infraestrutura que você tem autorização para tocar, o katana tem o formato de pipeline certo: encanamento STDIN/STDOUT, binário distribuível e modos sem navegador e com navegador. O merge em duas passagens foi necessário para as classes plantadas deste fixture; se os seus alvos precisam das duas passagens é algo que você deve descobrir a partir de páginas representativas.
Pule se você quer dados, e não endereços. O katana nunca vai te entregar uma tabela de produtos; ele entrega as URLs onde os produtos podem viver, e outra ferramenta faz a extração. Pule também se você precisa que um único comando esteja completo — o merge em duas passagens funciona bem em pipeline e é irritante no prompt. E, se sua enumeração depende de endpoints <loc> de sitemap enquanto mira IPs, verifique o que está recebendo antes de confiar na saída, porque neste fixture esse caminho não retornou nada.
Alternativas e o limite da extração
Katana é gratuito, tem licença MIT e é self-hosted. Ele deixa descoberta, escolha de modo, implantação do navegador e merge de resultados do seu lado da fronteira.
Dentro do open source, as comparações úteis são por tarefa, não por linguagem. Colly é a outra opção em Go, mas é uma biblioteca que você compila com seus próprios callbacks e não renderiza JavaScript. Crawl4AI roda um navegador real e produz Markdown para pipelines de LLM, o que é um output totalmente diferente. Se você estiver comparando vários deles de uma vez, nosso resumo de scrapers open source organiza as categorias lado a lado.
Divulgação: Thunderbit é o produto do publisher e não foi testado neste fixture do Katana. Ele fica a jusante na categoria de extração gerenciada, convertendo páginas em texto ou registros estruturados em vez de enumerar a superfície de endpoints de um alvo autorizado. Um workflow pode usar as duas categorias, mas esta análise traz evidências apenas do comportamento de descoberta do Katana.
Experimente Thunderbit para Extração de Dados da Web
Veredito
Use o katana quando o entregável for uma lista de endpoints para alvos que você está autorizado a rastrear e quando puder validar a cobertura de modo contra esses alvos. Neste fixture, o -jc padrão recuperou os literais JavaScript plantados, enquanto o headless recuperou o endpoint inserido no runtime DOM; os modos sem navegador testados pelo Katana não recuperaram esse caminho em runtime. O escopo padrão também manteve o segundo host não buscado, e as execuções sem navegador continuaram mesmo após o 500 e o link quebrado.
Os cuidados são operacionais: pode ser necessário um merge em duas passagens para classes mistas de endpoint, o headless levou cerca de cinco vezes mais tempo local, o resume de seed única recrawleia caminhos concluídos, e o recall de known files foi 0/2 contra o alvo IP. Esses são resultados do fixture na v1.6.1, não garantias sobre qualquer site. Eles bastam para definir quais checks uma avaliação de produção deve repetir.
Experimente Thunderbit para Extração de Dados da Web Get Started Free
FAQs
Onde o katana salva o arquivo de resume, e retomar pula páginas que eu já havia rastreado?
O checkpoint caiu em ~/.config/katana/resume-<xid>.cfg, e não em um resume.cfg no diretório de trabalho, como a ajuda da flag sugere. E não, ele não pula as páginas concluídas: o arquivo guarda apenas as seeds de URL em voo, então um crawl retomado de seed única buscou novamente todos os 11 caminhos de referência, inclusive os 10 já finalizados. Você obtém o mesmo conjunto final de endpoints, só não o tempo economizado.
Por que -kf all solicitou meu sitemap.xml mas não rastreou as URLs dentro dele?
Contra um alvo IP, o resultado é compatível com um limite de validação de escopo, e não com erro de flag. No código da v1.6.1, o parser de sitemap do Katana monta cada request <loc> sem repassar o hostname raiz, e a checagem de escopo DNS para hosts IP literal pode então rejeitar essa URL; eu não instrumentei a execução para confirmar esse mecanismo. O recall ficou em 0 em todas as combinações de flag, profundidade e seed que tentei. Uma regex de host personalizada em -fs segue um ramo de validação diferente e é a correção prevista pela origem — mas eu não consegui confirmá-la com -kf na minha máquina, então trate isso como não testado. Extrair as URLs <loc> manualmente e alimentar o katana com elas é o caminho em que eu confiaria hoje.
O que devo preservar ao relatar um teste de cobertura do Katana?
Registre a versão exata do Katana e o comando, incluindo a expressão -fs byte a byte; defina as classes de endpoint antes da execução; mantenha logs de hits do lado do servidor além do stdout; e separe comportamento medido de hipóteses baseadas em código-fonte. Para execuções headless, registre também a build do navegador — este teste não o fez, o que limita a reprodução.
Devo usar -jc, -headless ou ambos?
Escolha com base nas classes de endpoint de que você precisa. Neste fixture, o -jc padrão encontrou literais armazenados em um arquivo JavaScript, enquanto o headless encontrou o endpoint inserido no runtime DOM. Nenhum dos modos cobriu as duas classes sozinho, então uma execução em duas passagens seguida de deduplicação foi a escolha defensável para alvos mistos.
Uma URL com erro vai interromper o crawl? Não interrompeu neste teste controlado. O Katana continuou após uma resposta 500 e um link quebrado e ainda devolveu os outros caminhos alcançáveis. Isso não substitui contabilidade de erro em produção: mantenha logs de requisições falhas e defina uma taxa de falha aceitável para que um crawl parcialmente bem-sucedido não seja confundido com cobertura completa.


