Skip to content

Arcjet advanced bot signals provides browser-based bot detection using client-side telemetry. A WebAssembly module runs in the browser to collect signals about the environment to detect automation anomalies. The result is stored in the aj_signals cookie and evaluated server-side as part of the detectBot rule.

Advanced signals is ideal for detecting bots that execute JavaScript and interact with the page like a real user, such as headless browsers and sophisticated scrapers. It can identify automation that mimics human behavior and evades simpler bot detection methods.

Arcjet advanced bot signals is most effective on sensitive routes where you want to be sure the request comes from a real browser, such as form POST handlers, login and signup endpoints, and other routes that are common targets for abuse.

It works by setting a cookie to identify the browser with analysis results stored on Arcjet’s cloud. The client-side signals are then linked when server-side bot detection rules are evaluated. This approach provides robust bot detection without relying on CAPTCHAs or user challenges, creating a seamless experience for legitimate users while effectively blocking bots.

  1. A browser script loads and starts a WebAssembly-based signal collector.
  2. The collector gathers signals and sends them to the Arcjet API. Arcjet then returns a continue token, which is stored in the aj_signals cookie.
  3. That cookie remains valid for a period of time, so the collected signals can be reused on later requests instead of being collected again on every page load.
  4. On the next request, the browser sends the aj_signals cookie. An Arcjet detectBot rule can then read it server-side, for example in a form POST handler.
  5. If the signals suggest automation, or if the cookie is missing, Arcjet denies the request and includes ARCJET_SIGNALS as the denial reason.

Arcjet advanced bot signals are available on all paid plans and charged based on usage. For details, see the pricing page.

Load the signals script on every page that might lead into a sensitive route e.g. a lead or signup form. This ensures the aj_signals cookie is set before the user reaches the protected endpoint e.g. via the form submissions.

We recommend adding the script to every page. Usage billing is based on when you call the detectBot rule, not on when the script runs, so you won’t be charged for simply loading the script.

The script evaluates the browser environment and sets the aj_signals cookie, which is then read server-side by detectBot.

Use a detectBot rule to act on signals results. Signals is most useful on sensitive routes such as form POST handlers, login, and signup endpoints — routes where you care whether the request comes from a real browser.

Section titled “Block when signals fails (recommended for sensitive routes)”

With an empty allow list, all detected bots are denied — including requests that fail signals evaluation. This is the recommended configuration for high-sensitivity endpoints:

If the result indicates automation, the request is denied with ARCJET_SIGNALS in the denial reason. You can see this in your logs and in the Arcjet dashboard.

To block signals failures alongside other specific identifiers, add ARCJET_SIGNALS to the deny list. With a deny list, only the listed identifiers are blocked — any bot not in the list is allowed:

To collect signals data without blocking, add ARCJET_SIGNALS to the allow list. Signals are still evaluated but the result does not affect the decision:

Once you install the signals script, the aj_signals cookie is set for any browser that loads it. This means that if a request is missing the cookie, it’s a strong signal that the client is not a real browser — for example, a bot that doesn’t execute JavaScript or a scraper that targets your API directly.

You can enforce the cookie by using a filter rule to block any request.

The expression len(http.request.cookie["aj_signals"]) eq 0 matches any request where the cookie is missing or empty, and the deny list causes those requests to be blocked before detectBot is even evaluated. You can combine this with a detectBot rule to layer both checks on the same route.

When a filter rule denies a request, decision.reason.isFilter() returns true. This lets you distinguish a missing-cookie denial from a bot detection denial and return a more helpful response — for example, redirecting the user to an error page that explains cookies are required rather than returning a silent 403:

If your application sets a Content Security Policy (CSP), add the following directives to allow the signals script to load and run:

DirectiveValueReason
script-srchttps://signals-cdn.arcjet.comLoads the signals JavaScript and WebAssembly module from the Arcjet CDN
script-src'wasm-unsafe-eval'Required to execute the WebAssembly module
connect-srchttps://signals-cdn.arcjet.comFetches assets from the Arcjet CDN at runtime
connect-srchttps://signals.arcjet.comSends collected signals to the Arcjet API
frame-srchttps://signals-cdn.arcjet.comAllows collecting signals from different contexts

Nosecone is a library for setting secure HTTP headers, including CSP, in Next.js middleware and other frameworks. If you’re already using nosecone, extend the default directives to include the signals URLs:

middleware.ts
import * as nosecone from "@nosecone/next";
const noseconeConfig: nosecone.Options = {
...nosecone.defaults,
contentSecurityPolicy: {
...nosecone.defaults.contentSecurityPolicy,
directives: {
...nosecone.defaults.contentSecurityPolicy.directives,
scriptSrc: [
...nosecone.defaults.contentSecurityPolicy.directives.scriptSrc,
"https://signals-cdn.arcjet.com",
"'wasm-unsafe-eval'",
],
connectSrc: [
...nosecone.defaults.contentSecurityPolicy.directives.connectSrc,
"https://signals-cdn.arcjet.com",
"https://signals.arcjet.com",
],
frameSrc: [
"https://signals-cdn.arcjet.com",
],
},
},
};
export default nosecone.createMiddleware(noseconeConfig);

Discussion