# Set up bot protection

Turn on Cloudflare Turnstile so scripts cannot create accounts or make your app send emails, and add your own keys when the sign-in page is on your domain.

A bot is a program that fills in forms on its own. Pointed at a sign-in page, it can create thousands of fake accounts, or make your app send thousands of sign-in emails and texts in your name, which hurts your sender's reputation and can cost you money. Bot protection puts **Cloudflare Turnstile** in front of those forms: a check, run by Cloudflare, that tells people from programs. Most people see it pass by itself, or tick one box. Turn it on before your app goes live.

> **Note:**

- Nothing, while your sign-in page is on BuildBase's own sign-in address: BuildBase's Turnstile keys are used.
- On your own sign-in domain, such as `auth.acme.example` (set up on the **Domains** tab): a free Cloudflare account, to make a Turnstile widget and get its two keys. Your domain does not have to use Cloudflare.



## Step by step

1. In the console's left rail, choose **Users**, then **Authentication** under **Access**, and open the **Security** tab.
2. Find the **Bot protection** card. Its first line says which keys your page uses.
3. Under **When to check**, choose a mode. **Only when suspicious** is the right start for most apps.
4. Press **Save** at the top of the card.

![The Bot protection card on the Security tab: your sign-in page is on BuildBase's shared domain, so BuildBase's Turnstile keys are used and nothing needs setting up. Under When to check, Off, Always and Only when suspicious, with Only when suspicious chosen](/images/sign-in-setup/bot-protection/card-shared.webp)

| Mode                 | What is checked                                                                                                                     |
| -------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| Off (the default)    | Nothing                                                                                                                             |
| Always               | Every sign-up, every sign-in, and every emailed code or link before it is sent                                                      |
| Only when suspicious | Sign-ups and emailed codes and links, always. A password sign-in only after three failed sign-ins from one IP address in 15 minutes |

Sign-ups and emails are checked in both modes because that is where the cost is: a bot that signs up or asks for a code makes your app send an email. A bot guessing passwords only gets told no, and the account lock after 10 wrong passwords already slows it down. **Continue as guest** and phone codes are checked like a sign-up.

The **?** beside **When to check** says it in short:

![The note behind the "?" beside When to check: bots are programs that fill in forms on their own; Cloudflare Turnstile is a check on your sign-in page that tells people from bots, and most people see nothing or tick one box; if Cloudflare cannot be reached, people are let through and the server logs it](/images/sign-in-setup/bot-protection/note.webp)

## On your own sign-in domain: add your keys

A Turnstile key pair works only on the web addresses listed on it. BuildBase's keys list BuildBase's sign-in address, so once your sign-in page moves to a domain of yours, such as `auth.acme.example`, you need a pair of your own. Until you add one, nothing is checked there, whatever mode you chose, and the card says so in yellow.

![The Bot protection card once the sign-in page is on your own domain, auth.acme.example: a warning that bot protection is off because no Turnstile keys are set, the three modes, How to get your Turnstile keys open with six steps (open Turnstile in the Cloudflare dashboard, Add widget, add auth.acme.example under Hostname management with a Copy hostname button, Managed widget mode, Create, copy both keys here and Save), and empty Site key and Secret key boxes](/images/sign-in-setup/bot-protection/card-custom-domain.webp)

### At Cloudflare

These steps happen on Cloudflare's site. They follow the card's **How to get your Turnstile keys** guide.

1. Sign in to the Cloudflare dashboard and open **Turnstile**. The card's **Turnstile in the Cloudflare dashboard** link goes straight there. Make a free account first if you have none.
2. Choose **Add widget**, and name it after your app, such as `Acme Analytics sign-in`.
3. Under **Hostname management**, add the address your sign-in page runs on. The card shows it, here `auth.acme.example`, with a **Copy hostname** button.
4. For **Widget mode**, choose **Managed**.
5. Choose **Create**. Cloudflare shows the widget's **site key** and **secret key**.

| Copy this from Cloudflare | Paste it into this BuildBase field | Secret?                                      |
| ------------------------- | ---------------------------------- | -------------------------------------------- |
| Site key                  | **Site key**                       | No: your sign-in page shows it to Cloudflare |
| Secret key                | **Secret key**                     | Yes: keep it out of emails and chats         |

| Copy this from BuildBase                    | Paste it at Cloudflare  |
| ------------------------------------------- | ----------------------- |
| Your sign-in address, from the card's guide | **Hostname management** |

### In the console

6. Paste the two keys into **Site key** and **Secret key** on the card. The keys below are examples, not real ones.

![The Site key box holding the example key 0x4AAAAAAAExampleSiteKey, with a note to add the sign-in address to the widget's hostnames and an Open Turnstile in Cloudflare link, and the Secret key box with the secret hidden as dots, noting it is kept private and never shown again once saved](/images/sign-in-setup/bot-protection/keys-filled.webp)

7. Press **Save**. BuildBase asks Cloudflare whether it knows the secret key before it keeps it, so a key copied wrong is refused there and then, rather than quietly checking nothing.

After saving, the secret key is stored encrypted and never shown again: its box is empty, with a **Saved** badge and "Leave blank to keep the saved key". To change the keys, paste a new pair and save. **Remove keys** deletes both, after asking.

![After Save: the Site key box keeps the site key, the Secret key box is empty with a Saved badge and the words "Leave blank to keep the saved key", and a Remove keys button below](/images/sign-in-setup/bot-protection/keys-saved.webp)

![The note behind the "?" beside Secret key: the private half, which BuildBase's server sends to Cloudflare to confirm each check passed; it is on the same widget as the site key, checked with Cloudflare when you save, then stored encrypted and never shown again](/images/sign-in-setup/bot-protection/secret-key-note.webp)

## Attach it to your app

There is nothing to tick. Bot protection belongs to your organization and covers every app's sign-in page.

## Check the preview

The **Preview** on the **Sign-in page** tab does not draw the check. Use **Open the live page for Acme Analytics**, or **Test** on the **Settings** tab, and look at the **Create account** page.

## What people see

> **Note:**
  These guides were shot on a test setup that cannot reach Cloudflare, so there
  is no screenshot of the check itself. This is what it looks like.


- **Where it shows.** On **Create account** and on a page that emails a code or link, the check sits above the button from the start, across the width of the form. On a password-only sign-in page it shows from the start in **Always** mode. In **Only when suspicious** mode it appears only when a sign-in from that network needs it, and the sign-in goes on by itself once the check passes.
- **What it is.** A box drawn by Cloudflare, with Cloudflare's logo in it, light or dark to follow the visitor's own setting. In **Managed** mode it usually checks by itself in a second or two and shows a tick. When Cloudflare is unsure, it asks the person to tick a box first.
- **While it runs.** If someone presses the button before the check has passed, the page says "Complete the security check above to continue." and sends the form as soon as it passes.
- **If it cannot load**, for example on a network that blocks Cloudflare, the page says "The security check could not load. Check your connection and try again."

Each answer from the check works once and for a few minutes, so an answer copied from one request cannot be used for another.

If BuildBase's server cannot reach Cloudflare, or cannot read your keys, the request is let through and the server logs why. An outage should not stop your sign-ups, and the per-address limits and account locks still apply.

## In your app (SDK)

Nothing changes in your app: the check runs on the hosted sign-in page. If you build your own sign-up form against the API, it has to show the widget itself and send its answer. See [Bot Protection](/authentication/bot-protection#if-you-call-the-auth-api-yourself).

To watch for bots at work, subscribe to the `auth.signup_challenged` [webhook event](/webhooks/overview). It fires for every sign-up, code or guest that a check refused, and says which form and why. A sudden run of them is a script working through your page.

## Check it works

- [ ] The **Bot protection** card shows the mode you chose, saved.
- [ ] On your own domain: no yellow warning, and **Secret key** shows **Saved**.
- [ ] The widget's hostnames at Cloudflare include your sign-in address.
- [ ] **Create account** on the live page shows Cloudflare's check above the button.

## Things to watch out for

- **It is off until you turn it on.** Every organization starts at **Off**.
- **On your own domain, nothing is checked until you add your own keys.** Whatever mode you choose, a sign-in page on a custom domain with no Turnstile keys runs no check. The card says so in yellow.
- **When the answer cannot be had, people are let through.** If Cloudflare cannot be reached, or no longer knows your secret key (the widget was deleted, for example), the request is allowed and the server logs why. Your per-address limits and account locks still apply.
- **A free Cloudflare widget takes up to 10 hostnames.** Each hostname you add also covers its subdomains.
- **A check's answer works once, for 5 minutes.** Someone who leaves the form open longer is asked to do the check again: "The security check expired or did not pass."
- **Some networks block Cloudflare.** Company filters and content blockers can stop the check from loading. Those people see "The security check could not load." and have to try another network.
- **Only when suspicious checks password sign-ins late.** A password sign-in is checked only after three failed sign-ins from one network address within 15 minutes. Sign-ups and emailed codes are checked from the first try.

## If something goes wrong

| What you or the person sees                                                                                  | Why                                                                        | What to do                                              |
| ------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------- | ------------------------------------------------------- |
| "Bot protection is off: no Turnstile keys are set" on the card                                               | Your sign-in page is on your own domain and has no keys                    | Add a site key and secret key, as above                 |
| "Cloudflare does not recognise this secret key. Copy it again from the widget in your Cloudflare dashboard." | The secret key was copied wrong, or belongs to another widget              | Copy it again from the same widget                      |
| "Add the secret key too. Turnstile needs both." or "Add the site key that goes with this secret key."        | Only one of the two keys was filled in                                     | Fill in both                                            |
| The check never appears on your own domain, or Cloudflare shows an error in its box                          | The widget's **Hostname management** does not list your sign-in address    | Add the address at Cloudflare                           |
| "The security check could not load. Check your connection and try again."                                    | The person's browser cannot reach Cloudflare                               | Try another network, or turn off a blocker for the page |
| "Complete the security check above to continue."                                                             | The button was pressed before the check passed                             | Wait for the tick, or tick the box                      |
| "The security check expired or did not pass. Please try it again."                                           | The check's answer ran out, was used already, or Cloudflare turned it down | Do the check again                                      |
| "Please complete the security check to continue."                                                            | The request needed a check and sent none                                   | Do the check, then press the button again               |

## Next steps

- [Who can sign up](/sign-in-setup/sign-up-access) - refuse throwaway inboxes as well as bots.
- [The sign-in page](/sign-in-setup/sign-in-page) - move the page to your own domain.
- [Bot Protection reference](/authentication/bot-protection) - modes, self-hosted keys and the API.
- [Set up sign-in, step by step](/sign-in-setup/overview) - every sign-in guide, and the order to set them up in.
