Skip to content

Commit

Permalink
chore: Update user agent header examples (#326)
Browse files Browse the repository at this point in the history
  • Loading branch information
davidmytton authored Dec 18, 2024
1 parent a7a2fe0 commit c4e52cf
Show file tree
Hide file tree
Showing 34 changed files with 431 additions and 187 deletions.
32 changes: 27 additions & 5 deletions src/content/docs/bot-protection/concepts.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -40,17 +40,39 @@ possible to achieve 100% accuracy.

Requests without `User-Agent` headers can not be identified as any particular
bot and will be marked as an errored decision. Check `decision.isErrored()` and
decide if you want to allow or deny the request. Our recommendation is to block
requests without `User-Agent` headers because most legitimate clients always
send this header.
decide if you want to allow or deny the request.

Our recommendation is to block requests without `User-Agent` headers before
calling Arcjet because most legitimate clients always send this header:

```ts
if (!request.headers.get("User-Agent")) {
log.warn("request missing required user-agent header");
// Return a 400 Bad request error here
// Next.js example:
// return NextResponse.json({ error: "Bad request" }, { status: 400 });
// Node.js example:
// res.writeHead(400, { "Content-Type": "application/json" });
// res.end(JSON.stringify({ error: "Bad request" }));
}
```

An alternative approach is to check the error message after the decision is
made:

```ts
if (decision.isErrored()) {
if (decision.reason.message.includes("missing User-Agent header")) {
log.warn({ error: decision.reason.message }, "Arcjet user-agent warning");
if (decision.reason.message.includes("requires user-agent header")) {
log.warn(
{ error: decision.reason.message },
"request missing required user-agent header",
);
// You could return a 400 Bad request error here
// Next.js example:
// return NextResponse.json({ error: "Bad request" }, { status: 400 });
// Node.js example:
// res.writeHead(400, { "Content-Type": "application/json" });
// res.end(JSON.stringify({ error: "Bad request" }));
} else {
// Just log the error and continue
log.error({ error: decision.reason.message }, "Arcjet error");
Expand Down
3 changes: 2 additions & 1 deletion src/content/docs/reference/nestjs.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -373,11 +373,12 @@ If there is an error condition, Arcjet will return an `ERROR` `conclusion`.

```ts
if (decision.isErrored()) {
if (decision.reason.message.includes("missing User-Agent header")) {
if (decision.reason.message.includes("requires user-agent header")) {
// Requests without User-Agent headers can not be identified as any
// particular bot and will be marked as an errored decision. Most
// legitimate clients always send this header, so we recommend blocking
// requests without it.
// See https://docs.arcjet.com/bot-protection/concepts#user-agent-header
this.logger.warn("User-Agent header is missing");
throw new HttpException("Bad request", HttpStatus.BAD_REQUEST);
} else {
Expand Down
24 changes: 17 additions & 7 deletions src/content/docs/reference/nodejs.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -436,13 +436,23 @@ If there is an error condition, Arcjet will return an `ERROR` `conclusion`.

```ts
if (decision.isErrored()) {
// Fail open by logging the error and continuing
console.warn("Arcjet error", decision.reason.message);
// You could also fail closed here for very sensitive routes
//res.writeHead(503, { "Content-Type": "application/json" });
//res.end(
// JSON.stringify({ error: "Service Unavailable", reason: decision.reason }),
//);
if (decision.reason.message.includes("requires user-agent header")) {
// Requests without User-Agent headers can not be identified as any
// particular bot and will be marked as an errored decision. Most
// legitimate clients always send this header, so we recommend blocking
// requests without it.
// See https://docs.arcjet.com/bot-protection/concepts#user-agent-header
res.writeHead(400, { "Content-Type": "application/json" });
res.end(JSON.stringify({ error: "Bad Request", reason: decision.reason }));
} else {
// Fail open by logging the error and continuing
console.warn("Arcjet error", decision.reason.message);
// You could also fail closed here for very sensitive routes
//res.writeHead(503, { "Content-Type": "application/json" });
//res.end(
// JSON.stringify({ error: "Service Unavailable", reason: decision.reason }),
//);
}
}
```

Expand Down
17 changes: 13 additions & 4 deletions src/content/docs/reference/remix.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -436,10 +436,19 @@ If there is an error condition, Arcjet will return an `ERROR` `conclusion`.

```ts
if (decision.isErrored()) {
// Fail open by logging the error and continuing
console.warn("Arcjet error", decision.reason.message);
// You could also fail closed here for very sensitive routes
//throw new Response("Service unavailable", { status: 503 });
if (decision.reason.message.includes("requires user-agent header")) {
// Requests without User-Agent headers can not be identified as any
// particular bot and will be marked as an errored decision. Most
// legitimate clients always send this header, so we recommend blocking
// requests without it.
// See https://docs.arcjet.com/bot-protection/concepts#user-agent-header
throw new Response("Bad request", { status: 400 });
} else {
// Fail open by logging the error and continuing
console.warn("Arcjet error", decision.reason.message);
// You could also fail closed here for very sensitive routes
//throw new Response("Service unavailable", { status: 503 });
}
}
```

Expand Down
22 changes: 14 additions & 8 deletions src/snippets/bot-protection/reference/bun/Errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,21 @@ export default {
fetch: aj.handler(async (req) => {
const decision = await aj.protect(req);

// If the request is missing a User-Agent header, the decision will be
// marked as an error! You should check for this and make a decision about
// the request since requests without a User-Agent could indicate a crafted
// request from an automated client.
if (decision.isErrored()) {
// Fail open by logging the error and continuing
console.warn("Arcjet error", decision.reason.message);
// You could also fail closed here if the request is missing a User-Agent
//return new Response("Service unavailable", { status: 503 });
if (decision.reason.message.includes("requires user-agent header")) {
// Requests without User-Agent headers can not be identified as any
// particular bot and will be marked as an errored decision. Most
// legitimate clients always send this header, so we recommend blocking
// requests without it.
// See https://docs.arcjet.com/bot-protection/concepts#user-agent-header
console.warn("User-Agent header is missing");
return new Response("Bad request", { status: 400 });
} else {
// Fail open by logging the error and continuing
console.warn("Arcjet error", decision.reason.message);
// You could also fail closed here for very sensitive routes
//return new Response("Service unavailable", { status: 503 });
}
}

if (decision.isDenied()) {
Expand Down
22 changes: 14 additions & 8 deletions src/snippets/bot-protection/reference/bun/Errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,21 @@ export default {
fetch: aj.handler(async (req) => {
const decision = await aj.protect(req);

// If the request is missing a User-Agent header, the decision will be
// marked as an error! You should check for this and make a decision about
// the request since requests without a User-Agent could indicate a crafted
// request from an automated client.
if (decision.isErrored()) {
// Fail open by logging the error and continuing
console.warn("Arcjet error", decision.reason.message);
// You could also fail closed here if the request is missing a User-Agent
//return new Response("Service unavailable", { status: 503 });
if (decision.reason.message.includes("requires user-agent header")) {
// Requests without User-Agent headers can not be identified as any
// particular bot and will be marked as an errored decision. Most
// legitimate clients always send this header, so we recommend blocking
// requests without it.
// See https://docs.arcjet.com/bot-protection/concepts#user-agent-header
console.warn("User-Agent header is missing");
return new Response("Bad request", { status: 400 });
} else {
// Fail open by logging the error and continuing
console.warn("Arcjet error", decision.reason.message);
// You could also fail closed here for very sensitive routes
//return new Response("Service unavailable", { status: 503 });
}
}

if (decision.isDenied()) {
Expand Down
22 changes: 14 additions & 8 deletions src/snippets/bot-protection/reference/deno/Errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,21 @@ Deno.serve(
aj.handler(async (req) => {
const decision = await aj.protect(req);

// If the request is missing a User-Agent header, the decision will be
// marked as an error! You should check for this and make a decision about
// the request since requests without a User-Agent could indicate a crafted
// request from an automated client.
if (decision.isErrored()) {
// Fail open by logging the error and continuing
console.warn("Arcjet error", decision.reason.message);
// You could also fail closed here if the request is missing a User-Agent
//return new Response("Service unavailable", { status: 503 });
if (decision.reason.message.includes("requires user-agent header")) {
// Requests without User-Agent headers can not be identified as any
// particular bot and will be marked as an errored decision. Most
// legitimate clients always send this header, so we recommend blocking
// requests without it.
// See https://docs.arcjet.com/bot-protection/concepts#user-agent-header
console.warn("User-Agent header is missing");
return new Response("Bad request", { status: 400 });
} else {
// Fail open by logging the error and continuing
console.warn("Arcjet error", decision.reason.message);
// You could also fail closed here for very sensitive routes
//return new Response("Service unavailable", { status: 503 });
}
}

if (decision.isDenied()) {
Expand Down
3 changes: 2 additions & 1 deletion src/snippets/bot-protection/reference/nestjs/Errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,12 @@ export class PageController {
throw new HttpException("Forbidden", HttpStatus.FORBIDDEN);
}
} else if (decision.isErrored()) {
if (decision.reason.message.includes("missing User-Agent header")) {
if (decision.reason.message.includes("requires user-agent header")) {
// Requests without User-Agent headers can not be identified as any
// particular bot and will be marked as an errored decision. Most
// legitimate clients always send this header, so we recommend blocking
// requests without it.
// See https://docs.arcjet.com/bot-protection/concepts#user-agent-header
this.logger.warn("User-Agent header is missing");
throw new HttpException("Bad request", HttpStatus.BAD_REQUEST);
} else {
Expand Down
22 changes: 14 additions & 8 deletions src/snippets/bot-protection/reference/nextjs/ErrorsApp.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,21 @@ const aj = arcjet({
export async function GET(req) {
const decision = await aj.protect(req);

// If the request is missing a User-Agent header, the decision will be
// marked as an error! You should check for this and make a decision about
// the request since requests without a User-Agent could indicate a crafted
// request from an automated client.
if (decision.isErrored()) {
// Fail open by logging the error and continuing
console.warn("Arcjet error", decision.reason.message);
// You could also fail closed here if the request is missing a User-Agent
//return NextResponse.json({ error: "Service unavailable" }, { status: 503 });
if (decision.reason.message.includes("requires user-agent header")) {
// Requests without User-Agent headers can not be identified as any
// particular bot and will be marked as an errored decision. Most
// legitimate clients always send this header, so we recommend blocking
// requests without it.
// See https://docs.arcjet.com/bot-protection/concepts#user-agent-header
console.warn("User-Agent header is missing");
return new Response("Bad request", { status: 400 });
} else {
// Fail open by logging the error and continuing
console.warn("Arcjet error", decision.reason.message);
// You could also fail closed here for very sensitive routes
//return new Response("Service unavailable", { status: 503 });
}
}

if (decision.isDenied()) {
Expand Down
22 changes: 14 additions & 8 deletions src/snippets/bot-protection/reference/nextjs/ErrorsApp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,21 @@ const aj = arcjet({
export async function GET(req: Request) {
const decision = await aj.protect(req);

// If the request is missing a User-Agent header, the decision will be
// marked as an error! You should check for this and make a decision about
// the request since requests without a User-Agent could indicate a crafted
// request from an automated client.
if (decision.isErrored()) {
// Fail open by logging the error and continuing
console.warn("Arcjet error", decision.reason.message);
// You could also fail closed here if the request is missing a User-Agent
//return NextResponse.json({ error: "Service unavailable" }, { status: 503 });
if (decision.reason.message.includes("requires user-agent header")) {
// Requests without User-Agent headers can not be identified as any
// particular bot and will be marked as an errored decision. Most
// legitimate clients always send this header, so we recommend blocking
// requests without it.
// See https://docs.arcjet.com/bot-protection/concepts#user-agent-header
console.warn("User-Agent header is missing");
return NextResponse.json({ error: "Bad request" }, { status: 400 });
} else {
// Fail open by logging the error and continuing
console.warn("Arcjet error", decision.reason.message);
// You could also fail closed here for very sensitive routes
//return NextResponse.json({ error: "Service unavailable" }, { status: 503 });
}
}

if (decision.isDenied()) {
Expand Down
22 changes: 14 additions & 8 deletions src/snippets/bot-protection/reference/nextjs/ErrorsPages.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,21 @@ const aj = arcjet({
export default async function handler(req, res) {
const decision = await aj.protect(req);

// If the request is missing a User-Agent header, the decision will be
// marked as an error! You should check for this and make a decision about
// the request since requests without a User-Agent could indicate a crafted
// request from an automated client.
if (decision.isErrored()) {
// Fail open by logging the error and continuing
console.warn("Arcjet error", decision.reason.message);
// You could also fail closed here if the request is missing a User-Agent
//return res.status(503).json({ error: "Service unavailable" });
if (decision.reason.message.includes("requires user-agent header")) {
// Requests without User-Agent headers can not be identified as any
// particular bot and will be marked as an errored decision. Most
// legitimate clients always send this header, so we recommend blocking
// requests without it.
// See https://docs.arcjet.com/bot-protection/concepts#user-agent-header
console.warn("User-Agent header is missing");
return res.status(400).json({ error: "Bad request" });
} else {
// Fail open by logging the error and continuing
console.warn("Arcjet error", decision.reason.message);
// You could also fail closed here for very sensitive routes
//return res.status(503).json({ error: "Service unavailable" });
}
}

if (decision.isDenied()) {
Expand Down
22 changes: 14 additions & 8 deletions src/snippets/bot-protection/reference/nextjs/ErrorsPages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,21 @@ export default async function handler(
) {
const decision = await aj.protect(req);

// If the request is missing a User-Agent header, the decision will be
// marked as an error! You should check for this and make a decision about
// the request since requests without a User-Agent could indicate a crafted
// request from an automated client.
if (decision.isErrored()) {
// Fail open by logging the error and continuing
console.warn("Arcjet error", decision.reason.message);
// You could also fail closed here if the request is missing a User-Agent
//return res.status(503).json({ error: "Service unavailable" });
if (decision.reason.message.includes("requires user-agent header")) {
// Requests without User-Agent headers can not be identified as any
// particular bot and will be marked as an errored decision. Most
// legitimate clients always send this header, so we recommend blocking
// requests without it.
// See https://docs.arcjet.com/bot-protection/concepts#user-agent-header
console.warn("User-Agent header is missing");
return res.status(400).json({ error: "Bad request" });
} else {
// Fail open by logging the error and continuing
console.warn("Arcjet error", decision.reason.message);
// You could also fail closed here for very sensitive routes
//return res.status(503).json({ error: "Service unavailable" });
}
}

if (decision.isDenied()) {
Expand Down
25 changes: 16 additions & 9 deletions src/snippets/bot-protection/reference/nodejs/Errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,23 @@ const aj = arcjet({
const server = http.createServer(async function (req, res) {
const decision = await aj.protect(req);

// If the request is missing a User-Agent header, the decision will be
// marked as an error! You should check for this and make a decision about
// the request since requests without a User-Agent could indicate a crafted
// request from an automated client.
if (decision.isErrored()) {
// Fail open by logging the error and continuing
console.warn("Arcjet error", decision.reason.message);
// You could also fail closed here if the request is missing a User-Agent
//res.writeHead(503, { "Content-Type": "application/json" });
//res.end(JSON.stringify({ error: "Service unavailable" }));
if (decision.reason.message.includes("requires user-agent header")) {
// Requests without User-Agent headers can not be identified as any
// particular bot and will be marked as an errored decision. Most
// legitimate clients always send this header, so we recommend blocking
// requests without it.
// See https://docs.arcjet.com/bot-protection/concepts#user-agent-header
console.warn("User-Agent header is missing");
res.writeHead(400, { "Content-Type": "application/json" });
res.end(JSON.stringify({ error: "Bad request" }));
} else {
// Fail open by logging the error and continuing
console.warn("Arcjet error", decision.reason.message);
// You could also fail closed here for very sensitive routes
//res.writeHead(503, { "Content-Type": "application/json" });
//res.end(JSON.stringify({ error: "Service unavailable" }));
}
}

if (decision.isDenied()) {
Expand Down
Loading

0 comments on commit c4e52cf

Please sign in to comment.