Bot checks win
A headless browser announces itself a dozen ways. Patch one and the next check catches you, and the fingerprint you spent a week faking is stale by the following release.
Krawlify is a self-hosted SEO crawler that drives the Chrome you are already signed in to — real profile, real cookies, real sessions — over the standard DevTools Protocol. Four pieces, all MIT licensed, all running on your own machine.
Every workaround for bot protection eventually runs into the same wall: the browser doing the crawling isn't the browser you actually use.
A headless browser announces itself a dozen ways. Patch one and the next check catches you, and the fingerprint you spent a week faking is stale by the following release.
Copying a session out of your browser keeps a scraper alive until the session rotates, the IP changes, or the TLS fingerprint stops matching. Then you export them again.
Relaunching Chrome with --remote-debugging-port hands your whole profile to whatever connects — every tab, including the ones that have nothing to do with the crawl.
There is nothing to imitate, because the profile doing the crawling is the one already signed in. And there is no forked client to adopt — the relay presents an ordinary DevTools endpoint.
Use the app on its own. Add the extension and relay when a site fights back. Add the agent skill when you would rather ask for the audit than run it.
A multi-user website crawler with Screaming Frog–style data tabs, live progress streaming, a REST API, and an MCP server so an AI assistant can run audits itself.
<sitemapindex>, redirect chains, and status checks on external links and assets.datePublished and dateModified from schema.org markup, matched against sitemap <lastmod> per URL.crawl.completed on finish.# Pull the published image instead of building
docker pull kamenarov/krawlify-app:0.1.0
# Postgres, Redis, migrations, web and worker
IMAGE=kamenarov/krawlify-app:0.1.0 docker compose up -d
# → http://localhost:3000 — register, then start a crawl
Lets your scraping app drive this browser over the DevTools Protocol — in tabs it opens itself, never the ones you have open.
🕷 Krawlify label, and the debugger attaches to nothing else.chrome.storage.sync.
Permissions: debugger, tabs, tabGroups, storage,
alarms. Optional: cookies, <all_urls>.
The extension and the relay ship from one repository — they are two halves of the same thing.
On the Chrome Web Store. One click, updates handled by Chrome, no developer-mode prompt on every launch. For machines you administer there is still a signed CRX you host yourself and install by policy — and the source is there if you would rather not trust anyone's signature on something that attaches a debugger to your browser.
# ExtensionInstallForcelist — force-install from the Web Store
oonpeiliblmkimphjhhfaghpjbjmogon;https://clients2.google.com/service/update2/crx
A switchboard between your crawler and your browser. One Node process, one dependency, loopback-bound by default.
127.0.0.1 unless you opt out, with --tls-cert, --trust-proxy, --allow-host and --allow-origin for anything wider./json/version, /json/list, /json/new, /status, /healthz, plus the browser and page WebSockets.| Code | Meaning |
|---|---|
401 | No Authorization: Bearer header |
503 | Valid-looking token, but no live browser behind it |
404 | Wrong tenant GUID |
421 | Unexpected Host |
403 | Unexpected Origin |
# published image, amd64 + arm64
docker run -p 127.0.0.1:9333:9333 \
kamenarov/krawlify-relay:0.1.0
# or from a clone
npm start # → 127.0.0.1:9333
# liveness, unauthenticated
curl http://127.0.0.1:9333/healthz
# control channel: ws /control
status · tabs.list · tabs.create · tabs.close
tabs.activate · tabs.navigate · cookies.get*
# * requires --allow-cookies
Hand an assistant a list of URLs and get back one Excel workbook. A Claude Code agent skill with a CLI underneath — no server, no database, no build step.
🕷 Krawlify tab group. Launch mode (--launch) starts your installed Chrome on your own profile instead — no extension, no relay, nothing to set up.--wait-full holds it open for captchas and logins you solve by hand.--check runs all three without crawling anything.When the endpoint cannot hand over its cookie jar, the crawler fetches from inside the crawl tab and says so in the report — the redirects sheet is empty in that mode, and it tells you why rather than leaving a blank sheet to be misread. The app captures redirect chains in full.
# install once, from npm
npm install -g krawlify-agent
# register it as a Claude Code skill
krawlify-agent install-skill # ~/.claude/skills
krawlify-agent install-skill --project # ./.claude/skills
# no relay, no extension — just your Chrome
npx krawlify-agent --urls urls.txt --launch
# check the setup without crawling
krawlify-agent --check
# .env.local — relay mode only
KRAWLIFY_TOKEN=<the token from the extension>
KRAWLIFY_RELAY=http://127.0.0.1:9333
Output lands in your working directory as
krawlify-report-<host>-<YYYYMMDD-HHmm>.xlsx.
A running Krawlify at app.krawlify.com, seeded and signed-in-ready. Start a crawl, watch the results stream in, page through the thirteen tabs, export a workbook.
docker pull./playground.It is a shared, public sandbox. Treat everything in it as public. The account below is an administrator, and everyone signing in gets the same one — so anyone can see, change or delete anyone else's crawls, keys and settings.
Do not crawl anything confidential, do not paste a real API key, a real proxy password or a token from your own extension into it, and expect the whole instance to be reset without notice.
These are the credentials the project's own prisma:seed script creates. Running the
seed on your own install gives you the same pair — change the password on anything reachable
from outside your machine.
Start with the app alone. Add the browser path only when a site actually blocks you.
Postgres, Redis, migrations, the Next.js app and the crawl worker, in one command.
docker compose up --build
Visit http://localhost:3000 and create the first account. Or seed a demo user instead:
docker compose run --rm worker npm run prisma:seed
Paste a seed URL. Defaults are depth 10, 10,000 URLs, concurrency 8, a 15-second request timeout, and robots.txt respected. Results stream in as pages land.
You can skip this and use the hosted alpha.krawlify.com, which the extension is already pointed at — it is alpha, so treat it accordingly. Your own is one container, binds to loopback unless you say otherwise, and keeps the traffic yours.
docker run -p 127.0.0.1:9333:9333 kamenarov/krawlify-relay:0.1.0
# or, from a clone: npm start
Add it from the Chrome Web Store, then open the options page, set the Server URL and copy the token — your browser generates it, so the relay never had one to give you. The install page covers the enterprise-policy and unpacked routes.
http://127.0.0.1:9333 # your own
https://alpha.krawlify.com # the shipped default
In the app, tick Site uses Cloudflare / bot protection, set Browser server to your relay, Auth to bearer, and paste the extension's token. The token is not a server password — it selects whose browser the crawl runs in.
Tabs appear in your Chrome under a purple 🕷 Krawlify group, and close themselves when the crawl ends.
Published on npm with a provenance attestation, so you can verify it was built by the repository it claims to come from.
npm install -g krawlify-agent
Copies the skill into ~/.claude/skills/krawlify-agent. Start a new Claude Code session afterwards so it is picked up.
krawlify-agent install-skill
Launch mode needs nothing at all — it starts your installed Chrome on your own profile. Relay mode uses the Chrome you already have open; put KRAWLIFY_TOKEN and KRAWLIFY_RELAY in the skill's .env.local, or pass --token and --relay. Verify either with:
krawlify-agent --check
Hand it a list of URLs and it returns one workbook. Or run it directly:
krawlify-agent --urls urls.txt --launch
The relay presents an ordinary DevTools endpoint. If your crawler talks to a headless Chrome today, it talks to your real profile tomorrow — same call, different endpoint.
const browser = await puppeteer.connect({
browserWSEndpoint: 'ws://127.0.0.1:9333/devtools/browser/<guid>',
headers: { authorization: 'Bearer <token>' },
});
const browser = await chromium.connectOverCDP('http://127.0.0.1:9333', {
headers: { authorization: 'Bearer <token>' },
});
No forks, no patches, no custom client. One relay serves many browsers — the token in the header is what picks yours.
The same crawls and the same results, reachable three ways. API keys are created in Settings and shown once.
Thirteen tabs with server-side pagination, filter presets, sorting and URL search. Light, system or dark, applied before first paint.
Key-authenticated with Authorization: Bearer or X-API-Key. In-app docs at /docs/api, and a request runner at /playground that renders the equivalent curl.
POST /api/v1/crawls
GET /api/v1/crawls/:id
GET /api/v1/crawls/:id/results?tab=
DELETE /api/v1/crawls/:id
Streamable HTTP at /api/mcp, stateless, same API keys. Seven tools: list_crawls, get_crawl, get_crawl_results, list_result_tabs, start_crawl, stop_crawl, delete_crawl.
claude mcp add --transport http krawlify \
http://localhost:3000/api/mcp \
--header "Authorization: Bearer YOUR_KEY"
Driving a real browser through an extension buys a great deal, and costs a few things. All of them are worth knowing before you build on it.
Opening DevTools on a tab the crawler owns detaches Krawlify from it. Chrome allows a single debugger client per tab, and DevTools wins.
Target.createBrowserContext is not supported, so browser.newContext() and incognito windows are unavailable. The point is to use one real profile, so this is a consequence rather than an oversight.
Browser.setDownloadBehavior is accepted and then ignored, which means Playwright's download API will not work through the relay.
chrome:// pages, other extensions and the Chrome Web Store are outside what an extension may attach to. That is Chrome's boundary, not a setting.
While the extension is attached, Chrome displays its “started debugging this browser” notification. It cannot be suppressed, and anything claiming otherwise is doing something you should not want.
alpha.krawlify.com is the address the extension ships pointed at, so there is a working default rather than a blank field. It carries no uptime commitment, may change without notice, and may be withdrawn. Running your own is one container, and it keeps your crawl traffic off anyone else's machine.
Three repositories, two container images, one npm package and one Web Store listing. All MIT licensed, all published from tagged releases by CI rather than from someone's laptop.
The extension and the relay share one repository: neither is much use without the other.
Both built for linux/amd64 and linux/arm64, so they run unmodified on Apple silicon and on an ARM server.
Published with --provenance, so npm can attest which repository and workflow built the tarball. The relay is not on npm — it is distributed as a container.
oonpeiliblmkimphjhhfaghpjbjmogonThe self-hosted CRX on the install page is signed with a different key, so it carries a different ID. Pick one.
# the crawler and the relay, pinned
docker pull kamenarov/krawlify-app:0.1.0
docker pull kamenarov/krawlify-relay:0.1.0
# the agent skill
npm install -g krawlify-agent
Tags. :edge follows main and moves without warning — useful for trying something, wrong for anything you depend on. A v* git tag publishes the full semver set (0.1.0, 0.1, 0) and moves :latest. Pin the exact version in anything you deploy.
Yes. Krawlify is MIT-licensed software you run yourself. There are no plans, no tiers and no billing — the app, the relay and the agent skill are all free, and there is no account to create.
Yes — it is listed as Krawlify under Developer Tools, and that is the easiest way to install it. Two other routes stay supported: a signed CRX you host and force-install by enterprise policy, and a ZIP loaded unpacked. The self-hosted package is signed with a different key, so it has a different extension ID from the Web Store copy; install one or the other, not both.
No. The extension attaches Chrome's debugger only to tabs the automation opened itself, and groups them under a labelled tab group. Your own tabs are not filtered out of a list — they are not addressable in the protocol at all, so there is no request the crawler can make that reaches them.
A Manifest V3 extension cannot listen on a port, so it can only dial out. The relay is the fixed address both sides can reach: your crawler connects in, your browser connects out, and the relay matches them by the token in the request.
The chrome.debugger API is the only way to speak the DevTools Protocol to a normal Chrome that was not started with --remote-debugging-port. Without the extension you would have to relaunch your browser with a debugging port open, which exposes the entire profile — every tab, not just the crawler's.
Often, yes — because the page is fetched by a real Chrome with a real profile rather than a headless one. Krawlify can wait up to 60 seconds for an interstitial bot check to clear, then continue through the same cookie jar, IP and TLS fingerprint. Nothing is guaranteed, and you should only crawl sites you are permitted to crawl.
No. The extension ships pointed at the hosted relay, so there is a working default and nothing to set up beyond pasting your token. You will probably want your own anyway: it is a single Node process with one dependency, it ships with a Dockerfile and a compose file, it binds to 127.0.0.1 by default, and running it means no crawl traffic passes through anyone else's machine.
It is the hosted relay the extension ships pointed at, so the settings page has a working default instead of a blank field. It is alpha and the name is the warning: best effort, no uptime commitment, and it may change or be withdrawn. It is only a switchboard — it holds no browser state and writes no crawled page content to disk — but if a crawl matters to you, point the extension at a relay you run instead.
Nothing, unless you deliberately point the extension at our hosted relay. The extension has no telemetry, no analytics and no crash reporting, and the only connection it makes is to the relay address in its settings. This website loads no third-party resources and sets no cookies.
No. The relay presents an ordinary DevTools endpoint. If your code connects to a headless Chrome today with puppeteer.connect or chromium.connectOverCDP, the same call works against your real profile — you change the endpoint and add a Bearer header.
The app crawls a whole site and gives you a dashboard. The agent skill takes a list of URLs you already have and returns a single Excel workbook, which is what you want when an assistant is doing the audit for you and needs a file to hand back.
Yes — app.krawlify.com runs the real application, and the sign-in details are published on this page: [email protected] with the password demo1234. It is a shared public sandbox on a single administrator account, so everyone who signs in sees and can change everything. Do not put anything confidential into it, do not paste a real API key or a token from your own extension, and expect it to be reset without notice.
Three repositories on GitHub: krawlify-app, krawlify-relay (which holds both the extension and the relay) and krawlify-skill. The app and the relay are published to Docker Hub as kamenarov/krawlify-app and kamenarov/krawlify-relay, built for both amd64 and arm64. The agent skill is on npm as krawlify-agent. Every artefact is published by CI from a tagged release, never from a laptop.
Yes, and that is the intended way to run it. The app, the crawl worker, Postgres, Redis and the relay all come up from Docker Compose on your own machine or server. The only component that lives anywhere else is the extension, which lives in your browser.
Bring the stack up, register, and point it at a site. Add the browser path the first time something blocks you.