# In-Page Enforcement

Shield scores every visit and recommends `allow`, `monitor`, `challenge` or
`block`. With in-page enforcement switched on, the SDK acts on that
recommendation the moment a risky visitor submits a sign-up, login,
checkout, lead or password-reset form, on any install type: JavaScript
snippet, npm, Google Tag Manager, WordPress, WooCommerce or Shopify.

Nothing changes in your snippet. The setting travels inside the responses
the SDK already receives, so there is no extra request, and the decision
itself is made by Shield's servers, never by rules shipped to the browser.
Requires SDK 1.2.0 or later; the `v1.js` CDN URL always serves it.

## What it is, and what it is not

This is friction. It stops bots and casual abuse that run your page in a
real browser, which is most of it. It does not stop anyone who posts to
your endpoints directly, disables JavaScript, or edits the page. For the
sign-ups, logins and payments that matter, confirm the session on your
server with the [verify endpoint](https://www.findip.net/docs/shield/server-verification.md), one call from
your backend. It returns the same recommendation, and since SDK 1.2.0 also
whether the visitor passed a Turnstile challenge.

## Switch it on

Open the site in the Shield dashboard. Under **In the page (SDK)** the
enforcement pages are **Protected forms** (which forms Shield protects, and
how), **Custom rules** (your own decisions) and the **Enforcement log**.

Protection is per form. On **Protected forms**, press **Protect a form** and
answer three questions:

1. **Which form?** Pick one of the forms Shield has seen submitted on your
   site in the last 30 days (page, form id / name / action, how Shield
   classified it, how often it fired), or add a form by its page path if
   Shield has not seen it yet. Leaving the id, name and action blank
   protects every form on that page.
2. **What kind of form is it?** Sign-up, login, checkout and payment, email
   and contact, password reset, or other form. Shield's guess is
   preselected. Your answer is also the form's correction: from then on
   Shield classifies that form the way you said, on the page (SDK 1.6.0)
   and on ingest for sites pinned to an older SDK.
3. **How should Shield respond?** An *enforcement type*: **Monitor only**
   (record, never interfere), **Slow down**, **Verify (Turnstile)**,
   **Block**, or a custom type of your own (see below).

The page then lists your protected forms with their activity, the forms
Shield has seen but does not protect yet (protect or ignore each one with a
click), and your enforcement types. Every protected form can be paused,
edited or removed; a paused form keeps its category but is not enforced.
The **Protection is on / paused** switch at the top pauses everything at
once. Up to 150 forms can be protected per site, 50 per kind.

### Enforcement types

An enforcement type says what happens in the page for each of Shield's
verdicts:

| Shield says | Options |
|---|---|
| `block` (risky visitor) | **Stop** the submit and show your message · **Redirect** to a URL · Let it through |
| `challenge` (suspicious visitor) | **Turnstile check** · **Slow down** · **Stop** · Let it through |
| `monitor` (slightly unusual visitor) | Let it through · **Slow down** |

`allow` is never touched. The built-in types cover most sites: *Monitor
only* (nothing, nothing, nothing), *Slow down* (stop, slow down, nothing),
*Verify (Turnstile)* (stop, Turnstile check, nothing) and *Block* (stop,
stop, nothing), all with the default texts and a 5-second delay. A custom
type sets its own mix, the slow-down delay, a redirect URL and the texts
visitors see; create one from the wizard or duplicate a built-in one on the
Protected forms page. A type in use cannot be deleted.

Different forms can use different types on SDK 1.7.0 (`v1.js` always has
it). Sites pinned to an older SDK apply the type most of their protected
forms use to every protected form.

### Sites set up before per-form protection

Sites that used to protect whole form kinds ("every sign-up form Shield
recognises") keep working exactly as before until you choose the forms to
keep: the Protected forms page shows the forms Shield saw for those kinds,
pre-ticked, and confirming switches the site to per-form protection. Until
then, protecting new forms and pausing are unavailable.

## The four actions

- **Stop.** The submit is cancelled and your message appears in a small
  dialog floating over the page (centred, on a dimmed backdrop, so it is seen
  wherever the form sits; SDK 1.10.0 — earlier SDKs append it under the
  form). The card has the class `findip-shield-notice`, which you can style;
  the visitor closes it with its Close button, Escape or a click outside.
  The page's own submit handlers do not run. The texts for all three
  visible actions are yours to set: the stop message, the challenge message
  shown above the Turnstile widget, and the slow-down countdown text, where
  `{seconds}` is replaced by the remaining seconds (SDK 1.5.0).
- **Slow down.** The submit is cancelled, a countdown of the configured
  number of seconds appears in the same dialog, and the form is submitted once
  automatically when it reaches zero. Scripted signups that fire many
  submits per second get exactly one, late.
- **Challenge.** A Cloudflare Turnstile widget appears in the dialog. When
  the visitor completes it, the SDK sends the token to Shield, Shield
  verifies it with your Turnstile secret, records the pass on the session,
  and the form submits. The pass lasts for the rest of the session, so the
  visitor is not challenged again on the next form. Without a Turnstile
  site key this action falls back to slowing down.
- **Redirect.** As soon as Shield's decision for the page is `block`, the
  visitor is sent to the URL you configure, typically a page that explains
  the situation and offers a way to contact you. The SDK never redirects
  from the target page itself, so it cannot loop.

Every action is recorded on the event. The Events page shows a badge such
as *Blocked*, *Slowed*, *Challenged* or *Challenge passed* next to the
event name, so you can see how often enforcement fires and on which forms.

## How forms are matched

Paths are matched exactly: lowercase, without query string or fragment.
Shield identifies forms by their `id`, `name` or action path (SDK 1.3.0+),
as metadata only; field values are never collected. A protected form
matches when the page path and every key you gave match.

Forms Shield cannot recognise as one of the five kinds are reported as
*other forms*; protect them like any other form and pick *Other form* as
the kind. Forms you never want classified or protected go on the ignore
list (the **Ignore** button under suggestions) and can be taken off it
again on the same page.

A protected form's name, if you give one, shows next to its kind in
Events, the enforcement log and alerts.

## Custom rules

Rules let you decide for a specific user, visitor, session, network,
country or ASN, or for a submit velocity, regardless of Shield's score.
They live on the Custom rules page and apply at ingest, so the SDK's in-page
actions and the verify endpoint both honour them.

| Match | You enter | Notes |
|---|---|---|
| User | an email, a user ID, or a SHA-256 hash | Stored as a hash only. Uses the same hashing as `identify` without a salt; salted installs enter the hash. |
| Visitor | the visitor ID | From the Sessions page or the log. |
| Session | the session ID | |
| IP or network | an address or CIDR | IPv4 and IPv6. |
| Country | an ISO-2 code | |
| ASN | a number | |
| Submit velocity | form kind, max, minutes, per visitor or IP | Matches when a submit would be the N-th of that kind in the window. |

Rules are independent of the in-page switch: with "Enforce in the page"
off, a matching rule still changes the verdict shown in the dashboard,
pushed to the dataLayer and returned by the verify endpoint, but nothing is
stopped, slowed or challenged in the visitor's browser. The Custom rules page says so
prominently while the switch is off.

A rule can also override the site's in-page behaviour when it decides:
its own action (stop, slow down, challenge, redirect, or nothing in the
page), slow-down delay, stop, challenge and slow-down texts, and redirect
URL. Blank fields keep the site setting. Requires SDK 1.4.0 (1.5.0 for the
challenge and slow-down texts).

Each rule carries an action: **block**, **challenge**, **monitor**, or
**allow**. A matching rule replaces Shield's recommendation for that event
and adds `rule:<name>` to the risk reasons. If several rules match, an
**allow** rule wins, otherwise the strongest action does. Rules can expire,
and the page shows how many submits each one enforced (stopped, slowed,
challenged or redirected); events that merely matched, such as page
views, are not counted. The enforcement log offers
one-click "Block user", "Block visitor" and "Block IP" for 24 hours, 7 days
or permanently.

## The enforcement log

The Enforcement log page lists every submit the SDK stopped, slowed,
challenged or redirected: when, who (the visitor's email and user ID when identity
reveal is on, otherwise the visitor ID), which form on which page, what
happened, Shield's recommendation and reasons, and the network. It is the
place to check that a rule works and to see what the friction is catching.

## Turnstile setup

1. In your Cloudflare account open **Turnstile** and create a widget for
   your domain. The free plan is enough.
2. Copy the **site key** and **secret key** into **Settings › Cloudflare
   Turnstile** in Shield. The site key is shown to visitors; the secret never
   leaves Shield.
3. Give the forms the **Verify (Turnstile)** enforcement type, or a custom
   type whose response to `challenge` is the Turnstile check.

Shield loads Turnstile only when a challenge is actually needed, so pages
that never trigger one do not load it at all. If Cloudflare's verification
service cannot be reached, the challenge fails open and the submit goes
through; the outcome is recorded as such.

## Fail-open rules

The SDK never blocks by accident. It leaves a submit alone when:

- enforcement is off, or the page has not yet received Shield's response
  for this visit (for example a submit within the first few hundred
  milliseconds),
- the recommendation is `allow`, or the form is not in scope,
- the visitor already passed a challenge in this session,
- a challenge cannot be rendered or verified.

Forms submitted from JavaScript with `form.submit()` do not fire a submit
event and are therefore not intercepted; `requestSubmit()` and normal
button submits are.

## Combining it with your server

Put the session ID in a hidden field and call the verify endpoint from your
backend before creating the account or taking the payment:

```json
{
  "verified": true,
  "risk_status": "available",
  "risk_score": 72,
  "recommendation": "challenge",
  "challenge_passed": true,
  "challenge_passed_at": "2026-09-08T09:12:44.000Z"
}
```

A backend that accepts `challenge` only when `challenge_passed` is true,
and rejects `block`, turns the in-page friction into a decision no client
can bypass.

---

Canonical page: https://www.findip.net/docs/shield/enforcement · Markdown: https://www.findip.net/docs/shield/enforcement.md · All Shield docs in one file: https://www.findip.net/llms-full.txt
