Skip to content

Advanced bot signals

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 works best on sensitive routes where you want to be sure the request comes from a real browser. Examples include form POST handlers, login and signup endpoints, and other routes that are common targets for abuse.

It sets a cookie to identify the browser, and stores the analysis results in Arcjet’s cloud. Arcjet then links the client-side signals when it evaluates server-side bot detection rules. This approach helps detect bots without relying on CAPTCHAs or user challenges, so legitimate users are not interrupted.

  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 charged based on usage. For details, see the pricing page.

Load the signals script on every page that might lead into a sensitive route, such as a lead or signup form. That way, the aj_signals cookie is set before the user reaches the protected endpoint through a form submission.

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 aren’t charged for 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. If a request is missing the cookie, that is a strong signal that the client is not a real browser. Examples include a bot that doesn’t execute JavaScript and 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, you can redirect 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