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.
When to use advanced signals
Section titled “When to use advanced signals”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.
How it works
Section titled “How it works”- A browser script loads and starts a WebAssembly-based signal collector.
- The collector gathers signals and sends them to the Arcjet API. Arcjet then returns
a continue token, which is stored in the
aj_signalscookie. - 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.
- On the next request, the browser sends the
aj_signalscookie. An ArcjetdetectBotrule can then read it server-side, for example in a formPOSThandler. - If the signals suggest automation, or if the cookie is missing, Arcjet denies the
request and includes
ARCJET_SIGNALSas the denial reason.
Pricing
Section titled “Pricing”Arcjet advanced bot signals are available on all paid plans and charged based on usage. For details, see the pricing page.
Step 1: Add the script
Section titled “Step 1: Add the script”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.
Add the signals script anywhere in your HTML layout or template. The defer attribute
ensures the script runs after the page has loaded.
<script id="arcjet-signals" src="https://signals-cdn.arcjet.com/aj-sig.js" defer></script>Add the signals script anywhere in your HTML layout or template. The defer attribute
ensures the script runs after the page has loaded.
<script id="arcjet-signals" src="https://signals-cdn.arcjet.com/aj-sig.js" defer></script>Add the signals script anywhere in your HTML layout or template. The defer attribute
ensures the script runs after the page has loaded.
<script id="arcjet-signals" src="https://signals-cdn.arcjet.com/aj-sig.js" defer></script>Add the signals script anywhere in your HTML layout or template. The defer attribute
ensures the script runs after the page has loaded.
<script id="arcjet-signals" src="https://signals-cdn.arcjet.com/aj-sig.js" defer></script>Add the signals script anywhere in your HTML layout or template. The defer attribute
ensures the script runs after the page has loaded.
<script id="arcjet-signals" src="https://signals-cdn.arcjet.com/aj-sig.js" defer></script>Add the signals script anywhere in your HTML layout or template. The defer attribute
ensures the script runs after the page has loaded.
<script id="arcjet-signals" src="https://signals-cdn.arcjet.com/aj-sig.js" defer></script>Add the signals script anywhere in your HTML layout or template. The defer attribute
ensures the script runs after the page has loaded.
<script id="arcjet-signals" src="https://signals-cdn.arcjet.com/aj-sig.js" defer></script>Add the signals script anywhere in your HTML layout or template. The defer attribute
ensures the script runs after the page has loaded.
<script id="arcjet-signals" src="https://signals-cdn.arcjet.com/aj-sig.js" defer></script>Add the signals script to your root layout. The afterInteractive strategy
ensures the script loads after the page is hydrated.
import Script from "next/script";
export default function RootLayout({ children }) { return ( <html lang="en"> <body> {children} <Script id="arcjet-signals" src="https://signals-cdn.arcjet.com/aj-sig.js" strategy="afterInteractive" /> </body> </html> );}import Script from "next/script";
export default function RootLayout({ children,}: { children: React.ReactNode;}) { return ( <html lang="en"> <body> {children} <Script id="arcjet-signals" src="https://signals-cdn.arcjet.com/aj-sig.js" strategy="afterInteractive" /> </body> </html> );}The script evaluates the browser environment and sets the aj_signals cookie,
which is then read server-side by detectBot.
Step 2: Configure bot detection
Section titled “Step 2: Configure bot detection”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.
Block when signals fails (recommended for sensitive routes)
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:
import "jsr:@std/dotenv/load";
import arcjet, { detectBot } from "@arcjet/deno";
const aj = arcjet({ key: Deno.env.get("ARCJET_KEY")!, rules: [ detectBot({ mode: "LIVE", allow: [], // deny all bots by default, including signals failures }), ],});
Deno.serve( { port: 3000 }, aj.handler(async (req) => { const decision = await aj.protect(req);
if (decision.isDenied()) { return new Response(JSON.stringify({ error: "Forbidden" }), { status: 403, headers: { "Content-Type": "application/json" }, }); }
return new Response(JSON.stringify({ message: "success" }), { headers: { "Content-Type": "application/json" }, }); }),);import os
from arcjet import Mode, arcjet, detect_botfrom fastapi import FastAPI, Requestfrom fastapi.responses import JSONResponse
app = FastAPI()
aj = arcjet( key=os.getenv("ARCJET_KEY"), # Get your site key from https://app.arcjet.com rules=[ detect_bot( mode=Mode.LIVE, allow=[], # deny all bots by default, including signals failures ), ],)
@app.post("/contact")async def contact(request: Request): decision = await aj.protect(request)
if decision.is_denied(): return JSONResponse({"error": "Forbidden"}, status_code=403)
return {"message": "success"}import os
from arcjet import Mode, arcjet_sync, detect_botfrom flask import Flask, jsonify, request
app = Flask(__name__)
aj = arcjet_sync( key=os.getenv("ARCJET_KEY"), # Get your site key from https://app.arcjet.com rules=[ detect_bot( mode=Mode.LIVE, allow=[], # deny all bots by default, including signals failures ), ],)
@app.post("/contact")def contact(): decision = aj.protect(request)
if decision.is_denied(): return jsonify(error="Forbidden"), 403
return jsonify(message="success")import { detectBot } from "@arcjet/nest";// ...// This is part of the rules configured with withRule or a guard// ...detectBot({ mode: "LIVE", allow: [], // deny all bots by default, including signals failures});const { detectBot } = require("@arcjet/nest");// ...// This is part of the rules configured with withRule or a guard// ...detectBot({ mode: "LIVE", allow: [], // deny all bots by default, including signals failures});import arcjet, { detectBot } from "@arcjet/bun";import { env } from "bun";
const aj = arcjet({ key: env.ARCJET_KEY!, rules: [ detectBot({ mode: "LIVE", allow: [], // deny all bots by default, including signals failures }), ],});
export default { port: 3000, fetch: aj.handler(async (req) => { const decision = await aj.protect(req);
if (decision.isDenied()) { return new Response(JSON.stringify({ error: "Forbidden" }), { status: 403, headers: { "Content-Type": "application/json" }, }); }
return new Response(JSON.stringify({ message: "success" }), { headers: { "Content-Type": "application/json" }, }); }),};import arcjet, { detectBot } from "@arcjet/bun";import { env } from "bun";
const aj = arcjet({ key: env.ARCJET_KEY, rules: [ detectBot({ mode: "LIVE", allow: [], // deny all bots by default, including signals failures }), ],});
export default { port: 3000, fetch: aj.handler(async (req) => { const decision = await aj.protect(req);
if (decision.isDenied()) { return new Response(JSON.stringify({ error: "Forbidden" }), { status: 403, headers: { "Content-Type": "application/json" }, }); }
return new Response(JSON.stringify({ message: "success" }), { headers: { "Content-Type": "application/json" }, }); }),};import arcjet, { detectBot } from "@arcjet/next";import { NextResponse } from "next/server";
const aj = arcjet({ key: process.env.ARCJET_KEY!, rules: [ detectBot({ mode: "LIVE", allow: [], // deny all bots by default, including signals failures }), ],});
export async function POST(req: Request) { const decision = await aj.protect(req);
if (decision.isDenied()) { return NextResponse.json({ error: "Forbidden" }, { status: 403 }); }
return NextResponse.json({ message: "success" });}import arcjet, { detectBot } from "@arcjet/next";import { NextResponse } from "next/server";
const aj = arcjet({ key: process.env.ARCJET_KEY, rules: [ detectBot({ mode: "LIVE", allow: [], // deny all bots by default, including signals failures }), ],});
export async function POST(req) { const decision = await aj.protect(req);
if (decision.isDenied()) { return NextResponse.json({ error: "Forbidden" }, { status: 403 }); }
return NextResponse.json({ message: "success" });}import arcjet, { detectBot } from "@arcjet/next";
const aj = arcjet({ key: process.env.ARCJET_KEY, rules: [ detectBot({ mode: "LIVE", allow: [], // deny all bots by default, including signals failures }), ],});
export default async function handler(req, res) { const decision = await aj.protect(req);
if (decision.isDenied()) { return res.status(403).json({ error: "Forbidden" }); }
return res.status(200).json({ message: "success" });}import arcjet, { detectBot } from "@arcjet/next";import type { NextApiRequest, NextApiResponse } from "next";
const aj = arcjet({ key: process.env.ARCJET_KEY!, rules: [ detectBot({ mode: "LIVE", allow: [], // deny all bots by default, including signals failures }), ],});
export default async function handler( req: NextApiRequest, res: NextApiResponse,) { const decision = await aj.protect(req);
if (decision.isDenied()) { return res.status(403).json({ error: "Forbidden" }); }
return res.status(200).json({ message: "success" });}import arcjet, { detectBot } from "@arcjet/node";import http from "node:http";
const aj = arcjet({ key: process.env.ARCJET_KEY!, rules: [ detectBot({ mode: "LIVE", allow: [], // deny all bots by default, including signals failures }), ],});
const server = http.createServer(async (req, res) => { const decision = await aj.protect(req);
if (decision.isDenied()) { res.writeHead(403, { "Content-Type": "application/json" }); res.end(JSON.stringify({ error: "Forbidden" })); return; }
res.writeHead(200, { "Content-Type": "application/json" }); res.end(JSON.stringify({ message: "success" }));});
server.listen(3000);import arcjet, { detectBot } from "@arcjet/node";import http from "node:http";
const aj = arcjet({ key: process.env.ARCJET_KEY, rules: [ detectBot({ mode: "LIVE", allow: [], // deny all bots by default, including signals failures }), ],});
const server = http.createServer(async (req, res) => { const decision = await aj.protect(req);
if (decision.isDenied()) { res.writeHead(403, { "Content-Type": "application/json" }); res.end(JSON.stringify({ error: "Forbidden" })); return; }
res.writeHead(200, { "Content-Type": "application/json" }); res.end(JSON.stringify({ message: "success" }));});
server.listen(3000);import arcjet, { detectBot } from "@arcjet/remix";import type { ActionFunctionArgs } from "@remix-run/node";
const aj = arcjet({ key: process.env.ARCJET_KEY!, rules: [ detectBot({ mode: "LIVE", allow: [], // deny all bots by default, including signals failures }), ],});
export async function action(args: ActionFunctionArgs) { const decision = await aj.protect(args);
if (decision.isDenied()) { throw new Response("Forbidden", { status: 403 }); }
// process form submission return null;}
export default function ContactPage() { return <form method="post">{/* form fields */}</form>;}import arcjet, { detectBot } from "@arcjet/remix";
const aj = arcjet({ key: process.env.ARCJET_KEY, rules: [ detectBot({ mode: "LIVE", allow: [], // deny all bots by default, including signals failures }), ],});
export async function action(args) { const decision = await aj.protect(args);
if (decision.isDenied()) { throw new Response("Forbidden", { status: 403 }); }
// process form submission return null;}
export default function ContactPage() { return <form method="post">{/* form fields */}</form>;}import { env } from "$env/dynamic/private";import arcjet, { detectBot } from "@arcjet/sveltekit";import { error } from "@sveltejs/kit";import type { RequestEvent } from "@sveltejs/kit";
const aj = arcjet({ key: env.ARCJET_KEY!, rules: [ detectBot({ mode: "LIVE", allow: [], // deny all bots by default, including signals failures }), ],});
export async function POST(event: RequestEvent) { const decision = await aj.protect(event);
if (decision.isDenied()) { return error(403, "Forbidden"); }
return new Response(JSON.stringify({ message: "success" }), { headers: { "Content-Type": "application/json" }, });}import { env } from "$env/dynamic/private";import arcjet, { detectBot } from "@arcjet/sveltekit";import { error } from "@sveltejs/kit";
const aj = arcjet({ key: env.ARCJET_KEY, rules: [ detectBot({ mode: "LIVE", allow: [], // deny all bots by default, including signals failures }), ],});
export async function POST(event) { const decision = await aj.protect(event);
if (decision.isDenied()) { return error(403, "Forbidden"); }
return new Response(JSON.stringify({ message: "success" }), { headers: { "Content-Type": "application/json" }, });}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.
Deny signals explicitly
Section titled “Deny signals explicitly”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:
detectBot({ mode: "LIVE", deny: [ "ARCJET_SIGNALS", // Block requests that fail signals detection "CATEGORY:AI", // Block AI scrapers // All other bots are allowed ],}),detectBot({ mode: "LIVE", deny: [ "ARCJET_SIGNALS", // Block requests that fail signals detection "CATEGORY:AI", // Block AI scrapers // All other bots are allowed ],}),detectBot({ mode: "LIVE", deny: [ "ARCJET_SIGNALS", // Block requests that fail signals detection "CATEGORY:AI", // Block AI scrapers // All other bots are allowed ],}),detectBot({ mode: "LIVE", deny: [ "ARCJET_SIGNALS", // Block requests that fail signals detection "CATEGORY:AI", // Block AI scrapers // All other bots are allowed ],}),detectBot({ mode: "LIVE", deny: [ "ARCJET_SIGNALS", // Block requests that fail signals detection "CATEGORY:AI", // Block AI scrapers // All other bots are allowed ],}),detectBot({ mode: "LIVE", deny: [ "ARCJET_SIGNALS", // Block requests that fail signals detection "CATEGORY:AI", // Block AI scrapers // All other bots are allowed ],}),detectBot({ mode: "LIVE", deny: [ "ARCJET_SIGNALS", // Block requests that fail signals detection "CATEGORY:AI", // Block AI scrapers // All other bots are allowed ],}),detect_bot( mode=Mode.LIVE, deny=[ "ARCJET_SIGNALS", # Block requests that fail signals detection "CATEGORY:AI", # Block AI scrapers # All other bots are allowed ],)detect_bot( mode=Mode.LIVE, deny=[ "ARCJET_SIGNALS", # Block requests that fail signals detection "CATEGORY:AI", # Block AI scrapers # All other bots are allowed ],)Monitor without blocking
Section titled “Monitor without blocking”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:
detectBot({ mode: "LIVE", allow: [ "CATEGORY:SEARCH_ENGINE", // Allow search engine crawlers "ARCJET_SIGNALS", // Don't block on signals failures ],}),detectBot({ mode: "LIVE", allow: [ "CATEGORY:SEARCH_ENGINE", // Allow search engine crawlers "ARCJET_SIGNALS", // Don't block on signals failures ],}),detectBot({ mode: "LIVE", allow: [ "CATEGORY:SEARCH_ENGINE", // Allow search engine crawlers "ARCJET_SIGNALS", // Don't block on signals failures ],}),detectBot({ mode: "LIVE", allow: [ "CATEGORY:SEARCH_ENGINE", // Allow search engine crawlers "ARCJET_SIGNALS", // Don't block on signals failures ],}),detectBot({ mode: "LIVE", allow: [ "CATEGORY:SEARCH_ENGINE", // Allow search engine crawlers "ARCJET_SIGNALS", // Don't block on signals failures ],}),detectBot({ mode: "LIVE", allow: [ "CATEGORY:SEARCH_ENGINE", // Allow search engine crawlers "ARCJET_SIGNALS", // Don't block on signals failures ],}),detectBot({ mode: "LIVE", allow: [ "CATEGORY:SEARCH_ENGINE", // Allow search engine crawlers "ARCJET_SIGNALS", // Don't block on signals failures ],}),detect_bot( mode=Mode.LIVE, allow=[ "CATEGORY:SEARCH_ENGINE", # Allow search engine crawlers "ARCJET_SIGNALS", # Don't block on signals failures ],)detect_bot( mode=Mode.LIVE, allow=[ "CATEGORY:SEARCH_ENGINE", # Allow search engine crawlers "ARCJET_SIGNALS", # Don't block on signals failures ],)Step 3: Enforce with a filter rule
Section titled “Step 3: Enforce with a filter rule”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.
filter({ deny: ['len(http.request.cookie["aj_signals"]) eq 0'], mode: "LIVE",}),filter({ deny: ['len(http.request.cookie["aj_signals"]) eq 0'], mode: "LIVE",}),filter({ deny: ['len(http.request.cookie["aj_signals"]) eq 0'], mode: "LIVE",}),filter({ deny: ['len(http.request.cookie["aj_signals"]) eq 0'], mode: "LIVE",}),filter({ deny: ['len(http.request.cookie["aj_signals"]) eq 0'], mode: "LIVE",}),filter({ deny: ['len(http.request.cookie["aj_signals"]) eq 0'], mode: "LIVE",}),filter({ deny: ['len(http.request.cookie["aj_signals"]) eq 0'], mode: "LIVE",}),filter_request( deny=['len(http.request.cookie["aj_signals"]) eq 0'], mode=Mode.LIVE,)filter_request( deny=['len(http.request.cookie["aj_signals"]) eq 0'], mode=Mode.LIVE,)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.
Handling the filter decision
Section titled “Handling the filter decision”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 (decision.isDenied()) { if (decision.reason.isFilter()) { // The aj_signals cookie is missing — the request didn't come from a // browser that loaded the signals script. Return a helpful error so // the user knows to enable cookies rather than seeing a silent 403. return new Response("Cookies are required to submit this form", { status: 403, }); } return new Response("Forbidden", { status: 403 });}if (decision.isDenied()) { if (decision.reason.isFilter()) { // The aj_signals cookie is missing — the request didn't come from a // browser that loaded the signals script. Return a helpful error so // the user knows to enable cookies rather than seeing a silent 403. return new Response("Cookies are required to submit this form", { status: 403, }); } return new Response("Forbidden", { status: 403 });}if (decision.isDenied()) { if (decision.reason.isFilter()) { // The aj_signals cookie is missing — the request didn't come from a // browser that loaded the signals script. Return a helpful error so // the user knows to enable cookies rather than seeing a silent 403. return new Response("Cookies are required to submit this form", { status: 403, }); } return new Response("Forbidden", { status: 403 });}if (decision.isDenied()) { if (decision.reason.isFilter()) { // The aj_signals cookie is missing — the request didn't come from a // browser that loaded the signals script. Return a helpful error so // the user knows to enable cookies rather than seeing a silent 403. return new Response("Cookies are required to submit this form", { status: 403, }); } return new Response("Forbidden", { status: 403 });}if (decision.isDenied()) { if (decision.reason.isFilter()) { // The aj_signals cookie is missing — the request didn't come from a // browser that loaded the signals script. Return a helpful error so // the user knows to enable cookies rather than seeing a silent 403. return new Response("Cookies are required to submit this form", { status: 403, }); } return new Response("Forbidden", { status: 403 });}if (decision.isDenied()) { if (decision.reason.isFilter()) { // The aj_signals cookie is missing — the request didn't come from a // browser that loaded the signals script. Return a helpful error so // the user knows to enable cookies rather than seeing a silent 403. return new Response("Cookies are required to submit this form", { status: 403, }); } return new Response("Forbidden", { status: 403 });}if decision.is_denied(): if decision.reason.is_filter(): # The aj_signals cookie is missing — the request didn't come from a # browser that loaded the signals script. Return a helpful error so # the user knows to enable cookies rather than seeing a silent 403. return JSONResponse( {"error": "Cookies are required to submit this form"}, status_code=403, ) return JSONResponse({"error": "Forbidden"}, status_code=403)if decision.is_denied(): if decision.reason.is_filter(): # The aj_signals cookie is missing — the request didn't come from a # browser that loaded the signals script. Return a helpful error so # the user knows to enable cookies rather than seeing a silent 403. return JSONResponse( {"error": "Cookies are required to submit this form"}, status_code=403, ) return JSONResponse({"error": "Forbidden"}, status_code=403)import arcjet, { detectBot, filter } from "@arcjet/next";import { NextResponse } from "next/server";
const aj = arcjet({ key: process.env.ARCJET_KEY!, rules: [ filter({ deny: ['len(http.request.cookie["aj_signals"]) eq 0'], mode: "LIVE", }), detectBot({ mode: "LIVE", allow: [], }), ],});
export async function POST(req: Request) { const decision = await aj.protect(req);
if (decision.isDenied()) { if (decision.reason.isFilter()) { // The aj_signals cookie is missing — the request didn't come from a // browser that loaded the signals script. Redirect with a helpful error // rather than a silent 403 so the user can take action (e.g. enable // cookies). return NextResponse.redirect( new URL("/?error=CookiesRequired", req.url), { status: 303 }, ); } return NextResponse.rewrite(new URL("/denied", req.url), { status: 403 }); }
return NextResponse.json({ message: "success" });}import arcjet, { detectBot, filter } from "@arcjet/next";import { NextResponse } from "next/server";
const aj = arcjet({ key: process.env.ARCJET_KEY, rules: [ filter({ deny: ['len(http.request.cookie["aj_signals"]) eq 0'], mode: "LIVE", }), detectBot({ mode: "LIVE", allow: [], }), ],});
export async function POST(req) { const decision = await aj.protect(req);
if (decision.isDenied()) { if (decision.reason.isFilter()) { // The aj_signals cookie is missing — the request didn't come from a // browser that loaded the signals script. Redirect with a helpful error // rather than a silent 403 so the user can take action (e.g. enable // cookies). return NextResponse.redirect( new URL("/?error=CookiesRequired", req.url), { status: 303 }, ); } return NextResponse.rewrite(new URL("/denied", req.url), { status: 403 }); }
return NextResponse.json({ message: "success" });}Content Security Policy
Section titled “Content Security Policy”If your application sets a Content Security Policy (CSP), add the following directives to allow the signals script to load and run:
| Directive | Value | Reason |
|---|---|---|
script-src | https://signals-cdn.arcjet.com | Loads the signals JavaScript and WebAssembly module from the Arcjet CDN |
script-src | 'wasm-unsafe-eval' | Required to execute the WebAssembly module |
connect-src | https://signals-cdn.arcjet.com | Fetches assets from the Arcjet CDN at runtime |
connect-src | https://signals.arcjet.com | Sends collected signals to the Arcjet API |
frame-src | https://signals-cdn.arcjet.com | Allows collecting signals from different contexts |
Using nosecone
Section titled “Using nosecone”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:
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);