---
outline: deep
---
# Quickstart
Cap is a modern, lightweight, and self-hosted CAPTCHA alternative using SHA-256 proof-of-work and instrumentation challenges.
Unlike traditional CAPTCHAs, Cap is fast and unobtrusive, has no telemetry or tracking, and uses accessible proof-of-work instead of annoying visual puzzles.
We've found that Cap offers a better balance for site admins than big-tech alternatives because **it puts the levers of control in your hands, not a third party.** You decide the difficulty, you own the data, and you never pay per-request fees.
Cap consists of a client-side widget, which solves challenges and displays the checkbox, and a server-side component, which generates challenges and redeems solutions.
## 1. Setting up your server
We recommend starting with Cap Standalone. You're gonna need [Docker](https://docs.docker.com/get-docker/) and about 100 MB of free memory (it uses about 50 MB idle). It supports multiple site keys and is compatible with reCAPTCHA's siteverify API, so you can even run it alongside reCAPTCHA and switch over gradually.
Start by creating a `docker-compose.yml` file:
```yaml
version: "3.8"
services:
cap:
image: tiago2/cap:latest
container_name: cap
ports:
- "3000:3000"
environment:
ADMIN_KEY: your_secret_password
volumes:
- cap-data:/usr/src/app/.data
restart: unless-stopped
volumes:
cap-data:
```
::: tip Tips
- `ADMIN_KEY` is your dashboard login. We recommend making it at least 32 characters.
- Change `3000:3000` if that port is already in use on your host.
- If the dashboard is unreachable, try adding `network_mode: "host"` under the `cap` service.
:::
Start the container:
```bash
docker compose up -d
```
Open `http://localhost:3000` (or your server's IP/domain on port 3000) to access the dashboard. Log in with your admin key, create a site key, and note down both the **site key** and its **secret key** - you'll need both.
If you want the best protection and can handle higher memory usage, we also recommend turning on instrumentation challenges and potentially headless browser detection.
## 2. Adding the widget
You can find example snippets for multiple frameworks on the [widget docs](./widget.md#usage). We're gonna assume a basic vanilla implementation here for simplicity.
Add the widget script to your website's HTML:
```html
```
Then add the widget component, pointing it at your instance:
```html
```
- `` — the public URL of your Cap Standalone instance (e.g. `cap.example.com`)
- `` — the site key from your dashboard
Example:
```html
```
In your JavaScript, listen for the `solve` event to capture the token:
```js
const widget = document.querySelector("cap-widget");
widget.addEventListener("solve", function (e) {
const token = e.detail.token;
// Handle the token as needed
});
```
Alternatively, you can wrap the widget in a `` and Cap will automatically submit the token alongside other form data as `cap-token`.
You can also get a token programmatically without displaying the widget by using the [programmatic mode](./programmatic.md).
## 3. Verifying tokens
Once a user completes the CAPTCHA, your backend must verify the token before trusting it. Send a `POST` request to your instance's `/siteverify` endpoint:
::: code-group
```sh [curl]
curl "https:////siteverify" \
-X POST \
-H "Content-Type: application/json" \
-d '{ "secret": "", "response": "" }'
```
```js [fetch]
const { success } = await (
await fetch("https:////siteverify", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ secret: "", response: "" }),
})
).json();
```
```py [python]
import requests
success = requests.post(
"https:////siteverify",
json={"secret": "", "response": ""}
).json().get("success")
print(success)
```
```php [php]
//siteverify",
false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Content-Type: application/json",
"content" => json_encode(["secret"=>"","response"=>""])
]
])
), true);
var_dump($data['success'] ?? false);
```
:::
- `` — the secret key from your dashboard (**not** the dashboard admin key)
- `` — the token generated by the widget
A successful verification returns:
```json
{ "success": true }
```
That's it! Cap is fully set up. Your users solve challenges client-side, your server verifies tokens, and you own all the data.
## Next steps
**You're mostly done.** If you'd like, you can:
- [Customize your widget](./widget#options)'s look and feel
- [Fully configure Cap Standalone](./standalone/options.html) to set up CORS or make sure rate-limiting works properly