🌐 Detecting your location…
📢 Advertisement — Configure AdSense in Appearance → Customize → AdSense Settings

كيفية تنفيذ تحديد المعدل في واجهة برمجة تطبيقات Node.js: دليل 2026 الكامل

⏱️2 min read  ·  373 words

تحديد المعدل يحمي واجهة برمجة التطبيقات (API) الخاصة بك من سوء الاستخدام، ويمنع التحميل الزائد غير المقصود، ويضمن الاستخدام العادل بين العملاء. وبدون ذلك، يمكن لعميل واحد (أو مهاجم) أن يطغى على خادمك. يطبق هذا الدليل تحديدًا قويًا للمعدل في Node.js بدءًا من المستوى البسيط وحتى مستوى الإنتاج.

لماذا حد السعر؟

  • منع الإساءة: أوقف هجمات القوة الغاشمة والكشط
  • ضمان العدالة: لا يوجد عميل واحد يحتكر الموارد
  • تكاليف التحكم: الحد من العمليات باهظة الثمن (مكالمات الذكاء الاصطناعي واستعلامات قاعدة البيانات)
  • حماية الاستقرار: منع التحميل الزائد العرضي أو الضار

الخوارزميات الرئيسية

الخوارزمية كيف يعمل مقايضة
نافذة ثابتة N طلبات لكل نافذة زمنية محددة بسيطة، ولكنها تسمح بتدفقات عند حواف النافذة
نافذة منزلقة طلبات N في آخر X ثانية (متداول) أكثر سلاسة وأكثر تعقيدًا قليلاً
دلو الرمز المميز إعادة تعبئة الرموز مع مرور الوقت؛ كل طلب يكلف واحد يسمح برشقات نارية يمكن التحكم فيها ومرنة

بسيط: الحد الأقصى للمعدل

npm install express-rate-limit
const rateLimit = require('express-rate-limit');

const limiter = rateLimit({
  windowMs: 15 * 60 * 1000,   // 15 minutes
  max: 100,                    // 100 requests per window per IP
  standardHeaders: true,       // return RateLimit-* headers
  legacyHeaders: false,
  message: { error: 'Too many requests, please try again later.' },
});

// Apply globally
app.use(limiter);

// Or stricter limits on specific routes
const authLimiter = rateLimit({
  windowMs: 15 * 60 * 1000,
  max: 5,   // only 5 login attempts per 15 min
});
app.post('/login', authLimiter, loginHandler);

الإنتاج: تحديد المعدل المدعوم من Redis

لا تعمل حدود الذاكرة الداخلية عبر مثيلات الخادم المتعددة. حدود مشاركة Redis عبر جميع المثيلات:

npm install rate-limit-redis ioredis
const rateLimit = require('express-rate-limit');
const RedisStore = require('rate-limit-redis');
const Redis = require('ioredis');

const redis = new Redis(process.env.REDIS_URL);

const limiter = rateLimit({
  store: new RedisStore({
    sendCommand: (...args) => redis.call(...args),
  }),
  windowMs: 15 * 60 * 1000,
  max: 100,
});

app.use(limiter);
// Now all server instances share the same rate-limit counters

دلو رمزي مخصص مع Redis

async function tokenBucket(key, maxTokens, refillRate) {
  const now = Date.now();
  const bucket = await redis.hgetall(`bucket:${key}`);

  let tokens = parseFloat(bucket.tokens ?? maxTokens);
  let lastRefill = parseInt(bucket.lastRefill ?? now);

  // Refill tokens based on elapsed time
  const elapsed = (now - lastRefill) / 1000;
  tokens = Math.min(maxTokens, tokens + elapsed * refillRate);

  if (tokens < 1) {
    return { allowed: false, retryAfter: (1 - tokens) / refillRate };
  }

  tokens -= 1;  // consume one token
  await redis.hset(`bucket:${key}`, { tokens, lastRefill: now });
  await redis.expire(`bucket:${key}`, 3600);

  return { allowed: true, remaining: Math.floor(tokens) };
}

// Middleware
async function rateLimitMiddleware(req, res, next) {
  const result = await tokenBucket(req.ip, 10, 1);  // 10 tokens, 1/sec refill
  if (!result.allowed) {
    res.setHeader('Retry-After', Math.ceil(result.retryAfter));
    return res.status(429).json({ error: 'Rate limit exceeded' });
  }
  res.setHeader('X-RateLimit-Remaining', result.remaining);
  next();
}

تحديد لكل مستخدم (وليس فقط لكل IP)

// Rate limit by authenticated user ID instead of IP
const userLimiter = rateLimit({
  windowMs: 60 * 1000,
  max: 60,
  keyGenerator: (req) => {
    // Use user ID if authenticated, fall back to IP
    return req.user?.id || req.ip;
  },
  store: new RedisStore({ sendCommand: (...args) => redis.call(...args) }),
});

// Tiered limits based on plan
function getLimitForUser(req) {
  const plan = req.user?.plan || 'free';
  return { free: 100, pro: 1000, enterprise: 10000 }[plan];
}

إعداد رؤوس الاستجابة المناسبة

// Standard rate-limit headers help clients back off gracefully
res.setHeader('RateLimit-Limit', limit);
res.setHeader('RateLimit-Remaining', remaining);
res.setHeader('RateLimit-Reset', resetTimestamp);

// On limit exceeded, always include Retry-After
res.status(429)
   .setHeader('Retry-After', secondsUntilReset)
   .json({ error: 'Too many requests' });

أفضل الممارسات

  • استخدم Redis للتطبيقات متعددة المثيلات — لا تتم مزامنة حدود الذاكرة الداخلية عبر الخوادم
  • حدود أكثر صرامة على نقاط النهاية الحساسة – تسجيل الدخول، وإعادة تعيين كلمة المرور، ونقاط نهاية الدفع تحتاج إلى قيود صارمة
  • إرجاع رؤوس واضحة – يساعد RateLimit-* وRetry-After العملاء على التصرف بشكل جيد
  • الحد الأقصى للسعر من قبل المستخدم عند المصادقة – أكثر عدالة من IP فقط (عناوين IP المشتركة والوكلاء)
  • فكر في الحدود المتدرجة – حدود مختلفة للمستخدمين المجانيين مقابل المستخدمين المدفوعين
  • تتحد مع الدفاعات الأخرى — تحديد المعدل هو طبقة واحدة، وليس حلاً أمنيًا كاملاً

الأسئلة المتداولة

س: ما هي الخوارزمية التي يجب أن أستخدمها؟
ج: دلو الرمز المميز للمرونة (يسمح بالتدفقات التي يتم التحكم فيها)، والنافذة المنزلقة للنعومة، والنافذة الثابتة للبساطة. بالنسبة لمعظم واجهات برمجة التطبيقات، يعد الإعداد الافتراضي لحد المعدل السريع (نافذة ثابتة/منزلقة) أمرًا جيدًا. تناسب مجموعة الرمز المميز واجهات برمجة التطبيقات (APIs) التي تكون فيها الاندفاعات العرضية مقبولة.

س: هل يجب أن أضع حدًا للتقييم حسب عنوان IP أو المستخدم؟
ج: بواسطة معرف المستخدم عند المصادقة (أكثر عدلاً، ويتعامل مع عناوين IP المشتركة)، ويعود إلى IP للطلبات المجهولة. يمكن أن يؤثر تقييد IP فقط بشكل غير عادل على المستخدمين الذين يستخدمون NAT المشتركة أو وكلاء الشركات.

س: لماذا تتم إعادة ضبط حد السعر الخاص بي بشكل غير متوقع؟
ج: مع تحديد الذاكرة الداخلية عبر مثيلات متعددة، يكون لكل خادم عداد خاص به – يرى العميل الذي يصل إلى مثيلات مختلفة حدودًا غير متناسقة. استخدم Redis لمشاركة الحالة عبر جميع الحالات.

س: ما هو رمز الحالة الذي يجب أن أرجعه؟
ج: 429 طلبًا كثيرًا، معRetry-After رأس يخبر العميل بمدة الانتظار. هذا هو المعيار الذي يحترمه العملاء ذوو السلوك الجيد.

س: هل يمكن أن يؤدي تحديد المعدل إلى إيقاف هجمات DDoS؟
ج: إنه يساعد في مكافحة إساءة استخدام طبقة التطبيق ولكنه ليس دفاعًا كاملاً عن DDoS. بالنسبة للهجمات واسعة النطاق، استخدم CDN/WAF (Cloudflare، AWS Shield) على حافة الشبكة بالإضافة إلى تحديد معدل التطبيق.

الخلاصة

يعد تحديد المعدل أمرًا ضروريًا لحماية واجهة Node.js API الخاصة بك من سوء الاستخدام والتحميل الزائد. ابدأ بـحد المعدل السريع للحالات البسيطة، وانتقل إلىالحد المدعوم من Redis بالنسبة لتطبيقات الإنتاج متعددة المثيلات، فإن ذلك يحد من المزامنة عبر الخوادم. تطبيق حدود أكثر صرامة على نقاط النهاية الحساسة (تسجيل الدخول، والمدفوعات)، والحد الأقصى للمعدل من قبل المستخدم عند المصادقة، والعودة دائمًا واضحةRateLimit-* وRetry-After الرؤوس حتى يتراجع العملاء بأمان. ادمجها مع CDN/WAF للدفاع في العمق.

✍️ Leave a Comment

Your email address will not be published. Required fields are marked *

🌐 Read in:🇩🇪 Deutsch🇧🇷 Português🇸🇦 العربية🇮🇳 हिन्दी🇧🇩 বাংলা