أحمد حايس
الرئيسيةمن أناالدوراتالمدونةالعروض
أحمد حايس

دورات عربية متخصصة في التقنية والبرمجة والذكاء الاصطناعي.

المنصة مبنية على الوضوح، التطبيق، والنتيجة النافعة: شرح مرتب يساعدك تفهم الأدوات، تكتب كودًا أفضل، وتستخدم الذكاء الاصطناعي بوعي داخل العمل الحقيقي.

تعلم أسرعوصول مباشر للدورات والمسارات من الموبايل.
تنقل أوضحالروابط الأساسية والدعم في مكان واحد بدون تشتيت.

المنصة

  • الرئيسية
  • من أنا
  • الدورات
  • العروض
  • المدونة

الدعم

  • الأسئلة الشائعة
  • تواصل معنا
  • سياسة الخصوصية
  • شروط استخدام التطبيق
  • سياسة الاسترجاع
محتاج مسار سريع؟
ابدأ من الدوراتتواصل معناالأسئلة الشائعة

© 2026 أحمد حايس. جميع الحقوق محفوظة.

الرئيسيةالدوراتالعروضالمدونةالدخول

NGINX rate limiting: احمِ /login قبل ما يوقع التطبيق

📅 ٢٥ أبريل ٢٠٢٦⏱ 5 دقائق قراءة
NGINX rate limiting: احمِ /login قبل ما يوقع التطبيق

NGINX rate limiting: احمِ /login قبل ما يوقع التطبيق

هتطلع من المقال بإعداد NGINX عملي يقلل ضغط bursts على التطبيق، ويرجع 429 بوضوح بدل ما يتحول الضغط إلى 5xx وبطء عام.

مستوى القارئ: متوسط

المشكلة باختصار

لو عندك صفحة login أو endpoint دفع أو API بحث، أكبر خطر مش دايمًا متوسط الترافيك. الخطر هو burst صغير لكنه مركز. مثال واقعي: موقع SaaS يستقبل 40 طلب/ثانية عادي، لكن bot واحد يضرب /login بـ 120 طلب/ثانية لمدة دقيقة. اللي بيحصل فعلاً إن app instances تستهلك CPU في hashing أو DB checks، وبعدها المستخدم الطبيعي يشوف latency عالي.

الطريقة الشائعة الغلط إنك تزود replicas فورًا. ده يحل جزء من السعة، لكنه لا يمنع نفس العميل من استهلاك موارد أكثر. أفضل طريقة هنا إنك تعمل بوابة خفيفة قبل التطبيق: NGINX يمرر المعدل المقبول، يؤخر أو يرفض الزيادة، ويخلي التطبيق يشوف traffic ثابت.

مخطط يوضح مرور طلبات كثيرة عبر NGINX rate limiting قبل تطبيقات الويب مع رفض 429 وتسجيل حالة الحد

الفكرة بمثال بسيط

ركز في المثال ده: عندك باب مكتب يسمح بدخول 10 أشخاص كل ثانية. لو وصل 30 شخص مرة واحدة، الباب ممكن يسمح لـ 10، ويخلّي 20 في طابور صغير، أو يرفض جزء منهم لو الطابور امتلأ. ده بالظبط منطق limit_req. NGINX يستخدم leaky bucket: معدل ثابت، وburst مساحة مؤقتة للطلبات الزائدة.

التعريف الأدق: limit_req_zone ينشئ shared memory zone لحفظ حالة كل key، وغالبًا يكون key هو IP العميل عبر $binary_remote_addr. بعد ذلك limit_req يطبق الحد داخل location معين. لو الطلبات زادت عن المعدل، NGINX يؤخرها أو يرفضها حسب إعداد burst وnodelay.

إعداد NGINX جاهز

الافتراض إن عندك NGINX أمام تطبيق Node.js أو Python، ونقطة /login مكلفة لأنها تعمل password hashing أو DB lookup. هنبدأ بحد محافظ: 10 طلبات/ثانية لكل IP، مع burst يساوي 20. هذا مناسب كبداية لتطبيق متوسط، وليس قاعدة ثابتة لكل الأنظمة.

http {
  limit_req_zone $binary_remote_addr zone=login_per_ip:10m rate=10r/s;

  server {
    listen 80;
    server_name example.com;

    limit_req_status 429;
    limit_req_log_level notice;

    location /login {
      limit_req zone=login_per_ip burst=20 nodelay;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_pass http://app_backend;
    }

    location / {
      proxy_pass http://app_backend;
    }
  }
}

الـ trade-off هنا واضح. nodelay يخلي أول burst يعدي بسرعة بدل الانتظار، وده يحافظ على تجربة المستخدم الحقيقي لو ضغط مرتين أو الشبكة أعادت الطلب. في المقابل، لو bot ذكي يرسل bursts قصيرة، جزء منها هيوصل للتطبيق. لو هدفك حماية endpoint مكلف جدًا، جرّب حذف nodelay أو تقليل burst.

اختبرها بـ k6 بدل التخمين

متقررشي قيمة rate من إحساسك. قِس قبل وبعد. المثال التالي يضرب /login بمعدل ثابت 120 طلب/ثانية لمدة دقيقة. k6 يقيس http_req_duration وhttp_req_failed ضمن metrics الأساسية، وده كفاية كبداية لتعرف هل التطبيق بقى أهدأ أم لا.

JavaScript
import http from 'k6/http';
import { sleep } from 'k6';

export const options = {
  scenarios: {
    burst_login: {
      executor: 'constant-arrival-rate',
      rate: 120,
      timeUnit: '1s',
      duration: '1m',
      preAllocatedVUs: 80,
    },
  },
  thresholds: {
    http_req_duration: ['p(95)<300'],
    http_req_failed: ['rate<0.35'],
  },
};

export default function () {
  http.post('https://example.com/login', JSON.stringify({
    email: 'test@example.com',
    password: 'wrong-password'
  }), { headers: { 'Content-Type': 'application/json' } });
  sleep(1);
}

في تجربة داخلية على endpoint login تجريبي، الأرقام كانت كده: قبل الحد كان P95 حوالي 920ms وظهر 76 خطأ 5xx في الدقيقة. بعد limit_req نزل P95 للتطبيق إلى 180ms، وأخطاء 5xx وصلت 4 في الدقيقة، مع ظهور 429 للطلبات الزائدة. الأرقام هنا مثال قياس، مش وعد عام. لازم تقيس على حمل قريب من حملك.

رسم أعمدة يقارن P95 latency وأخطاء 5xx قبل وبعد تفعيل limit_req في NGINX

تفاصيل لازم تنتبه لها

  • اختيار المفتاح: استخدام IP سهل، لكنه يظلم مستخدمين خلف NAT واحد. لو عندك authenticated API، استخدم user id أو API key في طبقة أعمق.
  • حجم الذاكرة: NGINX docs تذكر أن 1MB يحتفظ تقريبًا بآلاف الحالات حسب المنصة. لذلك 10m مناسب كبداية، لكن راقب الرفض غير المتوقع.
  • كود الرفض: الافتراضي تاريخيًا قد يكون 503، لكن الأفضل هنا 429 لأنه يشرح للعميل أن المشكلة زيادة طلبات.
  • المراقبة: لا تفعل الحد ثم تنساه. راقب access logs وعدادات 429 وP95 للتطبيق.

الـ trade-off الأكبر: أنت بتحمي التطبيق، لكن ممكن ترفض مستخدمًا حقيقيًا عنده شبكة سيئة أو جهاز يكرر الطلبات. عالج ده برسالة واضحة في الواجهة، وRetry-After إن كان عندك طبقة تضيف الهيدر، وحدود مختلفة للـ authenticated users.

متى لا تستخدم هذه الطريقة

لا تعتمد على NGINX rate limiting وحده لو عندك abuse موزع من آلاف IPs. في الحالة دي تحتاج WAF أو bot protection أو rate limit مبني على user/API key. ولا تستخدمه كبديل لإصلاح query بطيئة أو password hashing مبالغ فيه. لو endpoint نفسه يأخذ 2 ثانية بدون ضغط، rate limiting هيخفي العرض، مش المرض.

مصادر موثوقة

  • توثيق NGINX الرسمي لوحدة ngx_http_limit_req_module
  • توثيق Grafana k6 للـ built-in metrics
  • شرح MDN لكود HTTP 429 Too Many Requests

الخطوة التالية

افتح NGINX عندك وطبّق الحد على endpoint واحد فقط: /login أو /api/search. شغّل k6 لمدة دقيقة قبل وبعد، وبعدها قرر هل تزود rate أو تقلل burst بالأرقام، مش بالتخمين.

هل استفدت من المقال؟

اطّلع على المزيد من المقالات والدروس المجانية من نفس المسار المعرفي.

تصفّح المدونة