تأمين تطبيق Next.js باستخدام Arcjet: تحديد المعدل وحماية البوتات وجدار حماية Shield في 2026

إطلاق تطبيق Next.js في 2026 بدون طبقة أمان هو دعوة مفتوحة للمتاعب. البوتات الآلية، وهجمات حشو بيانات الاعتماد، وإساءة استخدام الـ API، وهجمات حقن الأوامر في تطبيقات الذكاء الاصطناعي كلها رخيصة ومؤتمتة ولا تتوقف. كتابة وسيط مخصص لكل منها عملية بطيئة وهشة.
يوفر Arcjet حزمة أمان موجهة للمطورين تعمل داخل تطبيق Next.js الخاص بك. حزمة واحدة، واستدعاء واحد لـ protect()، وتحصل على تحديد المعدل، واكتشاف البوتات، وجدار حماية WAF عبر Shield، والتحقق من البريد الإلكتروني، وتصفية المعلومات الشخصية، وكشف حقن الأوامر — كلها كود، كلها قابلة للاختبار، وكلها بقواعد دقيقة لكل مسار على حدة.
في هذا الدليل ستضيف الحمايات الست إلى مشروع Next.js 15 باستخدام App Router، وتختبرها محلياً، ثم تنشرها بأمان إلى بيئة الإنتاج. بدون إعادة ضبط لنظام DNS، وبدون وسيط عكسي، وبدون بنية تحتية إضافية.
ما الذي ستبنيه
تطبيق Next.js 15 يحتوي على:
- نقطة نهاية عامة
/api/helloمحمية بواسطة Shield WAF، واكتشاف البوتات، وتحديد معدل النافذة الثابتة. - نقطة نهاية
/api/signupتحجب عناوين البريد الإلكتروني المؤقتة وتحدد عدد محاولات التسجيل لكل IP. - نقطة نهاية ذكاء اصطناعي
/api/chatتفرض ميزانية رموز، وتمنع حقن الأوامر، وتخفي المعلومات الحساسة. - لوحة تحكم في وقت حقيقي لقرارات الأمان في وحدة تحكم Arcjet.
المتطلبات الأساسية
قبل البدء، تأكد من توفر:
- Node.js الإصدار 20 أو أحدث
- مشروع Next.js 15 يستخدم App Router (أو اتبع الخطوة 1 لإنشاء واحد)
- حساب على Arcjet — الطبقة المجانية تكفي لهذا الدرس
- إلمام بـ TypeScript وبمعالجات المسارات في Next.js
- فهم أساسي لأكواد حالة HTTP وللوسطاء (middleware)
الخطوة 1: إنشاء مشروع Next.js 15
أنشئ مشروعاً جديداً (تخطَّ هذه الخطوة إن كان لديك واحد).
npx create-next-app@latest nextjs-arcjet-demo \
--typescript --app --tailwind --eslint \
--src-dir --import-alias "@/*"
cd nextjs-arcjet-demoشغّل خادم التطوير للتأكد من إقلاعه.
npm run devافتح http://localhost:3000 — يجب أن ترى صفحة Next.js الافتراضية.
الخطوة 2: تثبيت Arcjet
ثبّت حزمة Next.js من Arcjet.
npm install @arcjet/nextثم ثبّت المساعد الاختياري @arcjet/inspect (مفيد للتحقق من البوتات المزيفة).
npm install @arcjet/inspectالخطوة 3: الحصول على مفتاح Arcjet
سجّل الدخول إلى app.arcjet.com، وأنشئ موقعاً، وانسخ مفتاح الموقع (يبدأ بـ ajkey_).
أضفه إلى .env.local.
# .env.local
ARCJET_KEY=ajkey_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxيعمل Arcjet بأمان على طبقة الحافة: يبقى المفتاح على الخادم ولا يُرسل أبداً إلى المتصفح.
الخطوة 4: حماية مسار API عام
أنشئ الملف src/app/api/hello/route.ts.
// src/app/api/hello/route.ts
import arcjet, { detectBot, shield, fixedWindow } from "@arcjet/next";
import { NextResponse } from "next/server";
const aj = arcjet({
key: process.env.ARCJET_KEY!,
rules: [
shield({ mode: "LIVE" }),
detectBot({
mode: "LIVE",
allow: [
"CATEGORY:SEARCH_ENGINE",
"CATEGORY:MONITOR",
],
}),
fixedWindow({
mode: "LIVE",
window: "1m",
max: 20,
}),
],
});
export async function GET(req: Request) {
const decision = await aj.protect(req);
if (decision.isDenied()) {
if (decision.reason.isRateLimit()) {
return NextResponse.json(
{ error: "Too many requests" },
{ status: 429 },
);
}
if (decision.reason.isBot()) {
return NextResponse.json(
{ error: "Automated access is not allowed" },
{ status: 403 },
);
}
return NextResponse.json({ error: "Forbidden" }, { status: 403 });
}
return NextResponse.json({ message: "Hello, human." });
}تُفعَّل ثلاث قواعد في كل طلب:
- Shield يمنع هجمات OWASP Top 10 (حقن SQL، واجتياز المسارات، وحمولات XSS في سلاسل الاستعلام).
- detectBot يسمح بمحركات البحث الموثوقة وخدمات المراقبة، ويحجب كل شيء آخر، بما في ذلك curl وPuppeteer وChromium في وضع بدون واجهة.
- fixedWindow يحصر كل عميل في 20 طلب لكل دقيقة.
اختبر من جهازين طرفيين.
# الطرفية 1 — تابع القرارات في لوحة Arcjet
# الطرفية 2 — أرسل دفعة طلبات
for i in {1..25}; do curl -s -o /dev/null -w "%{http_code}\n" \
http://localhost:3000/api/hello; doneيجب أن ترى قرابة 20 استجابة بحالة 200، ثم 429 لبقية الطلبات.
الخطوة 5: حجب البوتات والسماح للمتصفحات الحقيقية
يوفر Arcjet تصنيفاً رأيياً للبوتات. قائمة جزئية:
| الفئة | أمثلة على العملاء |
|---|---|
CATEGORY:SEARCH_ENGINE | Googlebot، Bingbot، Yandex |
CATEGORY:MONITOR | Uptime Robot، Pingdom، BetterStack |
CATEGORY:PREVIEW | Slackbot، Discordbot، Twitterbot |
CATEGORY:AI | GPTBot، ClaudeBot، PerplexityBot |
للسماح بمعاينات الروابط على المنصات الاجتماعية وحجب زواحف الذكاء الاصطناعي، عدّل القاعدة.
detectBot({
mode: "LIVE",
allow: [
"CATEGORY:SEARCH_ENGINE",
"CATEGORY:MONITOR",
"CATEGORY:PREVIEW",
],
// deny: ["CATEGORY:AI"], // حجب صريح للذكاء الاصطناعي (اختياري)
}),بعض البوتات تكذب في قيمة User-Agent. استخدم isSpoofedBot للتحقق من IP الطلب مقابل نطاقات الـ IP المعروفة للبوت المُعلَن.
import { isSpoofedBot } from "@arcjet/inspect";
const decision = await aj.protect(req);
if (decision.results.some(isSpoofedBot)) {
return NextResponse.json(
{ error: "Spoofed bot detected" },
{ status: 403 },
);
}الخطوة 6: حماية نموذج التسجيل باستخدام protectSignup
نقاط نهاية التسجيل هدف رئيسي: حسابات وهمية، وحشو بيانات اعتماد، وقوائم سبام. يجمع Arcjet ثلاث قواعد في مساعد واحد.
أنشئ الملف src/app/api/signup/route.ts.
// src/app/api/signup/route.ts
import arcjet, { protectSignup } from "@arcjet/next";
import { NextResponse } from "next/server";
const aj = arcjet({
key: process.env.ARCJET_KEY!,
rules: [
protectSignup({
rateLimit: {
mode: "LIVE",
interval: "10m",
max: 5,
},
bots: {
mode: "LIVE",
allow: [],
},
email: {
mode: "LIVE",
deny: ["DISPOSABLE", "INVALID", "NO_MX_RECORDS"],
},
}),
],
});
export async function POST(req: Request) {
const body = await req.json();
const email = String(body.email ?? "");
const decision = await aj.protect(req, { email });
if (decision.isDenied()) {
if (decision.reason.isEmail()) {
return NextResponse.json(
{ error: "Invalid or disposable email" },
{ status: 400 },
);
}
if (decision.reason.isRateLimit()) {
return NextResponse.json(
{ error: "Too many signup attempts" },
{ status: 429 },
);
}
return NextResponse.json({ error: "Forbidden" }, { status: 403 });
}
return NextResponse.json({ ok: true });
}اختبر بعنوان بريد مؤقت.
curl -X POST http://localhost:3000/api/signup \
-H "Content-Type: application/json" \
-d '{"email":"throwaway@mailinator.com"}'
# → 400 Invalid or disposable email
curl -X POST http://localhost:3000/api/signup \
-H "Content-Type: application/json" \
-d '{"email":"real@noqta.tn"}'
# → 200 okتتحقق قاعدة البريد الإلكتروني من ثلاث طبقات: صحة الصياغة، ووجود سجلات MX، وقائمة النطاقات المؤقتة التي يديرها Arcjet.
الخطوة 7: حماية نقطة نهاية دردشة ذكاء اصطناعي
نقاط نهاية الذكاء الاصطناعي باهظة التكلفة وعرضة لمخاطر فريدة. طلب خبيث واحد قد يستنزف ميزانية الرموز أو يسرّب معلومات شخصية من موجه النظام. ادمج تحديد معدل دلو الرموز، وكشف حقن الأوامر، وتصفية المعلومات الحساسة.
أنشئ الملف src/app/api/chat/route.ts.
// src/app/api/chat/route.ts
import arcjet, {
detectBot,
detectPromptInjection,
sensitiveInfo,
shield,
tokenBucket,
} from "@arcjet/next";
import { NextResponse } from "next/server";
const aj = arcjet({
key: process.env.ARCJET_KEY!,
characteristics: ["userId"],
rules: [
shield({ mode: "LIVE" }),
detectBot({ mode: "LIVE", allow: [] }),
tokenBucket({
mode: "LIVE",
refillRate: 2_000,
interval: "1h",
capacity: 5_000,
}),
sensitiveInfo({
mode: "LIVE",
deny: ["CREDIT_CARD_NUMBER", "EMAIL", "PHONE_NUMBER"],
}),
detectPromptInjection({ mode: "LIVE", threshold: 0.5 }),
],
});
export async function POST(req: Request) {
const { userId, message } = (await req.json()) as {
userId: string;
message: string;
};
const estimate = Math.ceil(message.length / 4);
const decision = await aj.protect(req, {
userId,
requested: estimate,
sensitiveInfoValue: message,
detectPromptInjectionMessage: message,
});
if (decision.isDenied()) {
if (decision.reason.isRateLimit()) {
return NextResponse.json(
{ error: "AI usage limit exceeded" },
{ status: 429 },
);
}
if (decision.reason.isSensitiveInfo()) {
return NextResponse.json(
{ error: "Please remove personal info" },
{ status: 400 },
);
}
if (decision.reason.isPromptInjection()) {
return NextResponse.json(
{ error: "Prompt injection detected" },
{ status: 400 },
);
}
return NextResponse.json({ error: "Forbidden" }, { status: 403 });
}
return NextResponse.json({ reply: "pong" });
}السطر characteristics: ["userId"] بالغ الأهمية. بدونه يتشارك جميع المستخدمين دلو رموز واحد مفهرس بالـ IP — وقد يستنزف مستخدم قوي واحد الحصة للجميع. مع خاصية مربوطة بالمستخدم، يحصل كل حساب على ميزانية مستقلة.
الخطوة 8: استخدام وسيط Arcjet لحماية التطبيق كاملاً
حماية على مستوى المسار تكون صريحة لكنها متكررة. للتغطية الشاملة، ضع Arcjet في middleware.ts.
// middleware.ts
import arcjet, { createMiddleware, detectBot, shield } from "@arcjet/next";
export const config = {
matcher: ["/((?!_next/static|_next/image|favicon.ico).*)"],
};
const aj = arcjet({
key: process.env.ARCJET_KEY!,
rules: [
shield({ mode: "LIVE" }),
detectBot({
mode: "LIVE",
allow: ["CATEGORY:SEARCH_ENGINE", "CATEGORY:PREVIEW"],
}),
],
});
export default createMiddleware(aj);أبقِ قواعد الوسيط في حدها الأدنى. الميزات الحساسة للتكلفة مثل دلاء الرموز تنتمي للمسارات الباهظة، لا لكل جلب لأصل ثابت.
الخطوة 9: استخدم وضع DRY_RUN عند ضبط القواعد
لا تطلق قاعدة جديدة في وضع LIVE مباشرة. ابدأ بـ DRY_RUN ليسجل Arcjet القرار دون حجب المرور.
fixedWindow({
mode: "DRY_RUN",
window: "1m",
max: 20,
}),راقب لوحة Arcjet لبضعة أيام. إذا أظهر تدفق القرارات أن المرور المشروع فقط هو الذي يلامس الحد، فشدّد القاعدة. وحين تصبح البيانات سليمة، انقلها إلى LIVE.
الخطوة 10: اختبار التطبيق
يوفر Arcjet مساعد اختبار حتى لا تحتاج للاتصال بعناوين IP حقيقية في اختبارات الوحدة.
npm install -D @arcjet/test-utilsاكتب مواصفة للتحقق من أن قاعدة التسجيل ترفض البريد المؤقت.
// __tests__/signup.test.ts
import { POST } from "@/app/api/signup/route";
test("blocks disposable email", async () => {
const req = new Request("http://localhost/api/signup", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ email: "junk@mailinator.com" }),
});
const res = await POST(req);
expect(res.status).toBe(400);
});للاختبار المحلي الشامل لحدود المعدل، شغّل حلقة الخطوة 4 مع نافذة قصيرة (اضبط window: "10s" مؤقتاً) حتى لا تنتظر دقيقة كاملة لرؤية الخنق يعمل.
الخطوة 11: النشر إلى الإنتاج
لا يرتبط Arcjet بمنطقة معينة. انشر إلى Vercel أو Netlify أو Cloudflare Pages أو Fly.io أو خادم Node مستضاف ذاتياً — تعمل الحزمة بنفس الطريقة في كل مكان.
على Vercel، أضف ARCJET_KEY كمتغير بيئة من إعدادات المشروع وأعد النشر.
vercel env add ARCJET_KEY production
vercel --prodفي لوحة Arcjet افتح تبويب القرارات. يجب أن ترى حركة مرور حقيقية وأن يُحسب حكم كل قاعدة في كل دقيقة.
الخطوة 12: المراقبة والتنبيهات
تعرض اللوحة ثلاث إشارات تستحق المتابعة:
- حجم الطلبات لكل قاعدة — ارتفاع مفاجئ في إصابات
detectBotيعني عادة أن زاحفاً جديداً اكتشف موقعك. - نسبة الحجب — إذا تجاوزت نسبة حجب المستخدمين الحقيقيين أكثر من 1 بالمئة، فقواعدك صارمة جداً.
- نسبة البوتات المزيفة — ارتفاع النسبة يعني أن المهاجمين يدورون قيم User-Agent لينتحلوا Googlebot. فعّل التحقق عبر
isSpoofedBot.
اربط تكامل webhook بـ Slack أو PagerDuty لتُنبَّه عند تجاوز نسبة الحجب لعتبة محددة.
استكشاف الأخطاء وإصلاحها
كل الطلبات ترجع 403. تأكد أن قائمة السماح للبوتات تضم CATEGORY:SEARCH_ENGINE إن كنت تريد أن يصل إليك Googlebot. في التطوير المحلي يجب ألا يُحجب متصفحك أبداً — إن حصل ذلك، تحقق من أنك ضبطت mode: "DRY_RUN" على قاعدة detectBot ريثما تضبط قائمة السماح.
حدود المعدل لا تُصفَّر. تُفهرس خوارزميتا النافذة الثابتة ودلو الرموز بالـ IP افتراضياً. إذا كنت تختبر خلف NAT مؤسسي فكل المطورين يتشاركون نفس المفتاح. أضف characteristics: ["userId"] ومرّر معرّف المستخدم إلى استدعاء protect() ليكون الحد محصوراً بكل حساب.
Shield يعلّم استعلامات بحث مشروعة. قد يُفعَّل كشف OWASP على مدخلات نموذج تحوي كلمات شبيهة بـ SQL. انقل Shield إلى DRY_RUN لذلك المسار بعينه، راجع تبويب القرارات، وأضف استثناءات بإخراج القاعدة من الوسيط العام.
كاشف حقن الأوامر يفوّت هجمات كسر القيود. العتبة الافتراضية 0.5 على مقياس من 0 إلى 1. اخفضها إلى 0.3 لتصفية أشد، ثم راقب معدلات الإيجابيات الكاذبة. لحالات الدردشة العدائية، ادمج الكاشف مع تصلّب لموجه النظام — لا يوجد دفاع وحيد يوقف كل الهجمات.
الخطوات التالية
- استكشف قائمة بوتات Arcjet وابنِ قائمة سماح مخصصة لجمهورك.
- ادمج Arcjet مع Upstash Redis لتحديد المعدل والتخزين المؤقت حين تحتاج عدادات موزعة عبر عدة أحمال عمل.
- أضف ملاحظة تشغيل عبر تتبع ومراقبة OpenTelemetry لتربط قرارات Arcjet بزمن الاستجابة في الخدمات التالية.
- ضع طبقة Better Auth للمصادقة القوية، ثم استخدم
characteristicsفي Arcjet لحصر الحدود بكل مستخدم. - وسّع إلى دردشة الذكاء الاصطناعي عبر دليل Claude Agent SDK بحيث يمر كل استدعاء أداة عبر Arcjet أولاً.
الخاتمة
في أقل من ساعة أضفت ست طبقات حماية بمستوى إنتاج إلى تطبيق Next.js 15: جدار حماية، واكتشاف بوتات، وحدود معدل بنافذة ثابتة ودلو رموز، وتحقق من البريد الإلكتروني، وتصفية للمعلومات الشخصية، ودفاع ضد حقن الأوامر. كلها تعيش داخل قاعدة الكود، وكلها قابلة للاختبار، ولا تتطلب أياً منها وسيطاً عكسياً أو تغييراً في DNS.
الأمان ليس إعداداً لمرة واحدة. أطلق القواعد في DRY_RUN، راقب اللوحة، اضبط العتبات، ثم انقلها إلى LIVE حين تصفو الإشارة. ثم كرّر العملية — أضف قاعدة جديدة كلما أطلقت سطحاً جديداً.
ناقش مشروعك معنا
نحن هنا للمساعدة في احتياجات تطوير الويب الخاصة بك. حدد موعدًا لمناقشة مشروعك وكيف يمكننا مساعدتك.
دعنا نجد أفضل الحلول لاحتياجاتك.
مقالات ذات صلة

Better Auth مع Next.js 15: الدليل الشامل للمصادقة في 2026
تعلم كيفية تنفيذ نظام مصادقة متكامل في Next.js 15 باستخدام Better Auth. يغطي هذا الدليل تسجيل الدخول بالبريد الإلكتروني وOAuth والجلسات وحماية المسارات والتحكم بالأدوار.

إضافة المصادقة لتطبيق Next.js 15 باستخدام Auth.js v5: البريد الإلكتروني وOAuth والتحكم بالأدوار
تعلم كيفية إضافة نظام مصادقة جاهز للإنتاج لتطبيق Next.js 15 باستخدام Auth.js v5. يغطي هذا الدليل الشامل تسجيل الدخول عبر Google OAuth وبيانات الاعتماد بالبريد الإلكتروني وكلمة المرور والمسارات المحمية والتحكم بالوصول حسب الأدوار.

مفاتيح المرور و WebAuthn مع Next.js 15: بناء مصادقة بدون كلمة مرور في 2026
تعلم كيفية تطبيق المصادقة بدون كلمة مرور باستخدام مفاتيح المرور (Passkeys) وواجهة WebAuthn في تطبيق Next.js 15. يغطي هذا الدليل تسجيل بيانات الاعتماد وتسجيل الدخول البيومتري والتحقق من جانب الخادم والنشر للإنتاج.