Skip to content

Commit 8724160

Browse files
committed
fix(arcjet): move Arcjet to middleware to prevent re-renders in layout
1 parent 0de7725 commit 8724160

File tree

6 files changed

+90
-92
lines changed

6 files changed

+90
-92
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -380,7 +380,7 @@ Arcjet is configured with two main features: bot detection and the Arcjet Shield
380380
- [Bot detection](https://docs.arcjet.com/bot-protection/concepts) is configured to allow search engines, preview link generators e.g. Slack and Twitter previews, and to allow common uptime monitoring services. All other bots, such as scrapers and AI crawlers, will be blocked. You can [configure additional bot types](https://docs.arcjet.com/bot-protection/identifying-bots) to allow or block.
381381
- [Arcjet Shield WAF](https://docs.arcjet.com/shield/concepts) will detect and block common attacks such as SQL injection, cross-site scripting, and other OWASP Top 10 vulnerabilities.
382382

383-
Arcjet is configured with a central client at `src/libs/Arcjet.ts` that includes the Shield WAF rules. Additional rules are configured in `src/app/[locale]/layout.tsx` based on the page type.
383+
Arcjet is configured with a central client at `src/libs/Arcjet.ts` that includes the Shield WAF rules. Additional rules are applied when Arcjet is called in `middleware.ts`.
384384

385385
### Useful commands
386386

package-lock.json

Lines changed: 56 additions & 55 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
"prepare": "husky"
3131
},
3232
"dependencies": {
33-
"@arcjet/next": "1.0.0-beta.3",
33+
"@arcjet/next": "1.0.0-beta.4",
3434
"@clerk/localizations": "^3.13.1",
3535
"@clerk/nextjs": "^6.12.9",
3636
"@electric-sql/pglite": "^0.2.17",

src/app/[locale]/layout.tsx

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import type { Metadata } from 'next';
22
import { PostHogProvider } from '@/components/analytics/PostHogProvider';
33
import { DemoBadge } from '@/components/DemoBadge';
4-
import arcjet, { detectBot, request } from '@/libs/Arcjet';
5-
import { Env } from '@/libs/Env';
64
import { routing } from '@/libs/i18nNavigation';
75
import { NextIntlClientProvider } from 'next-intl';
86
import { getMessages, setRequestLocale } from 'next-intl/server';
@@ -38,20 +36,6 @@ export function generateStaticParams() {
3836
return routing.locales.map(locale => ({ locale }));
3937
}
4038

41-
// Improve security with Arcjet
42-
const aj = arcjet.withRule(
43-
detectBot({
44-
mode: 'LIVE',
45-
// Block all bots except the following
46-
allow: [
47-
// See https://docs.arcjet.com/bot-protection/identifying-bots
48-
'CATEGORY:SEARCH_ENGINE', // Allow search engines
49-
'CATEGORY:PREVIEW', // Allow preview links to show OG images
50-
'CATEGORY:MONITOR', // Allow uptime monitoring services
51-
],
52-
}),
53-
);
54-
5539
export default async function RootLayout(props: {
5640
children: React.ReactNode;
5741
params: Promise<{ locale: string }>;
@@ -64,22 +48,6 @@ export default async function RootLayout(props: {
6448

6549
setRequestLocale(locale);
6650

67-
// Verify the request with Arcjet
68-
if (Env.ARCJET_KEY) {
69-
const req = await request();
70-
const decision = await aj.protect(req);
71-
72-
// These errors are handled by the global error boundary, but you could also
73-
// redirect or show a custom error page
74-
if (decision.isDenied()) {
75-
if (decision.reason.isBot()) {
76-
throw new Error('No bots allowed');
77-
}
78-
79-
throw new Error('Access denied');
80-
}
81-
}
82-
8351
// Using internationalization in Client Components
8452
const messages = await getMessages();
8553

src/libs/Arcjet.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import arcjet, { shield } from '@arcjet/next';
22
import { Env } from './Env';
3-
import { logger } from './Logger';
43

54
// Re-export the rules to simplify imports inside handlers
65
export {
@@ -26,5 +25,4 @@ export default arcjet({
2625
}),
2726
// Other rules are added in different routes
2827
],
29-
log: logger,
3028
});

src/middleware.ts

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import type { NextFetchEvent, NextRequest } from 'next/server';
2+
import arcjet, { detectBot } from '@/libs/Arcjet';
3+
import { Env } from '@/libs/Env';
24
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server';
35
import createMiddleware from 'next-intl/middleware';
46
import { NextResponse } from 'next/server';
@@ -18,10 +20,39 @@ const isAuthPage = createRouteMatcher([
1820
'/:locale/sign-up(.*)',
1921
]);
2022

21-
export default function middleware(
23+
// Improve security with Arcjet
24+
const aj = arcjet.withRule(
25+
detectBot({
26+
mode: 'LIVE',
27+
// Block all bots except the following
28+
allow: [
29+
// See https://docs.arcjet.com/bot-protection/identifying-bots
30+
'CATEGORY:SEARCH_ENGINE', // Allow search engines
31+
'CATEGORY:PREVIEW', // Allow preview links to show OG images
32+
'CATEGORY:MONITOR', // Allow uptime monitoring services
33+
],
34+
}),
35+
);
36+
37+
export default async function middleware(
2238
request: NextRequest,
2339
event: NextFetchEvent,
2440
) {
41+
// Verify the request with Arcjet
42+
if (Env.ARCJET_KEY) {
43+
const decision = await aj.protect(request);
44+
45+
// These errors are handled by the global error boundary, but you could also
46+
// redirect or show a custom error page
47+
if (decision.isDenied()) {
48+
if (decision.reason.isBot()) {
49+
throw new Error('No bots allowed');
50+
}
51+
52+
throw new Error('Access denied');
53+
}
54+
}
55+
2556
// Run Clerk middleware only when it's necessary
2657
if (
2758
isAuthPage(request) || isProtectedRoute(request)

0 commit comments

Comments
 (0)