Bot Protection never acts on its own: your server applies the action it answers. The mode decides what that action is.
Monitor mode
Every new account starts here. Every answer's action is allow, so nobody is stopped, and intended_action says what block mode would do. The dashboard's overview counts how many decisions block mode would have blocked and challenged. Put the call in, deploy, and watch for a few days.
Block mode
Block mode turns on once one of your domains is protected: your server's first decision on a visitor of your pages who is not a bot adds it, and the challenge page sends visitors back only to a protected domain. While block mode is on, your last protected domain cannot be dropped. On your own machine it turns on once the tag ran on a page on localhost: until one of your domains is protected, the challenge page then sends back only visitors on that machine. You can also add a domain by hand on the Set-up page.
Turn it on in the Mode card of the Set-up page, from the next request. The action is then the one you set for the visitor's class:
| Class | Default action | Means |
|---|---|---|
| human high | allow | a person, with proof from outside the session |
| human | allow | a person your own feedback vouched for |
| unknown | allow | not enough evidence either way; most first visits |
| bot | challenge | automation, from what the client does |
| bot high | block | automation, with strong evidence |
You can change any class's action in the same card. A switch back to monitor mode is as quick.
What each action means for your code
allow: go on as if Bot Protection were not there.challenge: ask for more before going on: your CAPTCHA, an email or phone check, a slower path. With none, go on.block: refuse the action. Answer with the same message a failed sign-up shows, so a bot learns nothing.
Block mode never stops a page from loading. Bot Protection judges only the actions your server asks about (a sign-up, a login, a checkout), so a blocked bot still sees your site; the action is what it cannot complete.
The challenge page
Without a check of your own, send a challenged visitor to ours. In block mode, an answer whose action is challenge carries challenge.url: redirect the browser there, with &r= and the address to come back to, on one of your domains. The page has the browser solve a small proof of work, a few seconds at most, then sends it back to that address with kz_token added. The middleware keeps that token as the _kz cookie, and for the next 30 minutes that session's challenge is allow, with challenge.passed true.
The SDKs do it for you: on a page load (GET) without onChallenge, protect, withKorzen and Go's Protect send the visitor there, and when it comes back keep kz_token and send it on to the same address without it. They take the token only from a browser they sent to the page, so a link with someone else's token in it plants nothing. A form post or an API call under challenge is refused with 403: challenge the page that shows the form, and its pass lets the post through. A hook of yours sends the visitor with challengeRedirect(d, returnTo) (in Go, korzen.SendToChallenge), which marks the browser the same way. The address to come back to is the one the visitor asked for, on the host and scheme of X-Forwarded-Host and X-Forwarded-Proto when your proxy sets them, else of Host. Behind a proxy that forwards Host: localhost and neither, as nginx does by default, give publicURL (in Go, PublicURL) your site's origin, such as https://shop.example. The page sends a visitor back to localhost only when the tag saw the visitor's page on localhost too, and the IP of the decision was loopback or a private address (an app in Docker on your machine sees the bridge's, such as 172.17.0.1). Behind a proxy, a visitor's page is on your domain, so the page never sends them to localhost.
A browser without JavaScript waits instead: a few seconds, more for a harder puzzle, and the page goes on by itself and gives the same pass. There is no way back that skips the page. The proof of work, or the wait, makes every automated session pay for its sign-up; on its own, it does not stop a determined bot.
An action that needs the tag
A script that copies a browser and sends each request from a new address never runs your page, so it is unknown, not bot, as a person whose ad blocker stops the tag is. On an action where that is not enough, such as joining a queue, a sign-up or a checkout, send "require_token": true with the decision (the SDKs' requireToken option, in Go RequireToken). Without a valid token from the tag, the answer is challenge with token_required true, whatever the class; the class itself does not change, and in monitor mode it is the intended_action. A token takes the action once an hour per session, spent by the form's post, never by the page load that shows it: up to three tries within two minutes are the same attempt (a declined card, a typo), and a later one is answered as if there were no token, so one browser is one place in the queue. The SDKs' option sends it with posts only.
A visitor sent to the challenge page from such an action has no wait without JavaScript: the page asks for the proof of work and for our tag to run there, and they give a new token for the action: a script that solves the proof of work without running our tag gets no pass. People with JavaScript off cannot pass it, so send require_token only on the actions that need it. A form post under challenge gets the SDKs' 403: to send the visitor to the page instead, do it from onChallenge with challengeRedirect(d, returnTo) to the form's page (in Go, korzen.SendToChallenge). When Bot Protection is down or slow, your server still lets the visitor through.
require_token raises the cost of taking an action without a real visit: the visitor's browser must run our tag on your page (or on the challenge page) and earn a token bound to that browser and network, spent once. It is not a CAPTCHA and not a proof of a person: a well-funded bot that imitates a browser can still earn tokens, slowly and at a cost, and our classification and farm detection judge those sessions as any other.
Pausing
The protection switch on the Set-up page pauses everything without a deploy: every answer is allow, nothing is recorded or billed.