
Arcjet product philosophy: products & primitives
How do you design a security product for developers when they allegedly don't care about security?
Arcjet filters let you block requests using expressions over HTTP headers, IP addresses, and other request fields.
Arcjet allows developers to quickly implement sophisticated protections against bots, scrapers, and spammers with our security SDK. In just a few lines of code you can protect signup forms, login routes, and other sensitive areas of your application with easy local-testing of security rules that work the same way in production.
However, sometimes it's necessary to implement more basic protections by blocking requests based on simple characteristics. What if you want to only allow user logins from a specific company VPN network? Or block specific high-risk countries? Or validate that requests have specific headers?
As part of the latest beta-12 release of our JS SDK, Arcjet Filters let you block requests using Wireshark-like display filter expressions over HTTP headers, IP addresses, and other request fields. This allows you to quickly enforce rules like allow/deny by country, network, or user-agent
.
The expression language is based on Wireshark-like display filter expressions. Like other programming languages, there are fields (ip.src.vpn
), functions (lower()
), operators (or
, matches
), and values ("curl"
).
For example, to deny VPN or Tor traffic and any user agent header (converted to lower case) that matches curl or is not present, we can use this expression:
ip.src.vpn or
ip.src.tor or
lower(http.request.headers["user-agent"]) matches "curl" or
len(http.request.headers["user-agent"]) eq 0
Implementing this in middleware will block requests before your route handlers execute, for example in Next.js:
import arcjet, { createMiddleware, filter } from "@arcjet/next";
// Get your Arcjet key at <https://app.arcjet.com>.
// Set it as an environment variable instead of hard coding it.
const arcjetKey = process.env.ARCJET_KEY;
if (!arcjetKey) {
throw new Error("Cannot find `ARCJET_KEY` environment variable");
}
const aj = arcjet({
characteristics: ['http.request.headers["user-agent"]', "ip.src"],
key: arcjetKey,
rules: [
filter({
// This will deny any traffic using a VPN, Tor, that matches the curl
// user agent, or that has no user agent
deny: [
'ip.src.vpn or ip.src.tor or lower(http.request.headers["user-agent"]) matches "curl" or len(http.request.headers["user-agent"]) eq 0',
],
// Block requests with `LIVE`, use `DRY_RUN` to log only.
mode: "LIVE",
}),
],
});
export default createMiddleware(aj);
export const config = {
matcher: ["/((?!_next/static|_next/image|favicon.ico).*)"],
};
Or as a full Next.js function handler where you have more flexibility to adjust the user flow with custom logic:
import arcjet, { filter } from "@arcjet/next";
import { NextResponse } from "next/server";
const aj = arcjet({
key: process.env.ARCJET_KEY!,
rules: [
filter({
deny: [
// Requests matching this expression will be denied. All other
// requests will be allowed.
'ip.src.vpn or ip.src.tor or lower(http.request.headers["user-agent"]) matches "curl" or len(http.request.headers["user-agent"]) eq 0',
],
mode: "LIVE",
}),
],
});
export async function GET(req: Request) {
const decision = await aj.protect(req);
if (decision.isDenied()) {
return NextResponse.json({ error: "Forbidden" }, { status: 403 });
}
return NextResponse.json({
message: "Hello world",
});
}
Arcjet provides detailed IP address metadata within the decision response, but the filters expression language makes it easy to manipulate the underlying data and return immediate allow or deny decisions without having to write the logic in your own code.
This is available to all Arcjet users across all supported runtimes (Node, Bun, Deno) and frameworks (Express, Remix, Astro, SvelteKit, NestJS, Fastify, Next.js, React Router) now by upgrading to the latest SDK release.
Check out the quick start guide and filters reference documentation.
How do you design a security product for developers when they allegedly don't care about security?
How we implement different layers to secure our developer laptops & environments: Devcontainers, outbound firewall, macOS Transparency Consent and Control framework, and SSH agent for Git keys.
How to detect Next.js middleware bypass exploits (CVE-2025-29927 & CVE‑2024‑51479) in request logs and using Arcjet for incident forensics.
Get the full posts by email every week.