SSD Nodes Learn 🎉 VPS من $5.50/شهر
الأدلة Matt Connorبقلم Matt Connor

حل أخطاء 429 وحدود المعدل في SearXNG

تظهر أخطاء SearXNG 429 بسبب محدِّدك المحلي أو حظر محركات البحث عنوان IP. تعلّم قراءة السجل خلال دقيقة واختيار الإصلاح الصحيح دون تخمين.

لماذا يعرض SearXNG أخطاء 429

يعرض مثيل SearXNG مستضاف ذاتياً أخطاء 429 لسببين غير مرتبطين، وعادةً لا يكون حد المعدل الذي تحتاج إلى إصلاحه هو الحد الذي تفترضه. السبب الأول محلي: قرر محدِّد المعدل الخاص بـSearXNG أن الطلب صادر عن روبوت، فأجاب عنه Too Many Requests بحالة 429. السبب الثاني من المنبع: رفض أحد محركات البحث عنوان IP الخاص بخادمك، ويظهر ذلك للمستخدمين على شكل صفحة نتائج تفتقد بعض العناصر، لا على شكل خطأ 429.

لا يوجد إصلاح مشترك للحالتين. محدِّد المعدل تابع لك، لذلك يمكنك تغييره. أما الحظر من المنبع فيحدث لدى Google، ولذلك لن يرفعه أي إعداد في settings.yml. تخبرك السجلات بأي حالة تواجهها خلال نحو دقيقة، فابدأ من هناك.

يفترض هذا الدليل استخدام تثبيت الحاوية الموضح في مثيل SearXNG مستضاف ذاتياً على VPS الخاص بك. تأتي جميع أسماء الإعدادات أدناه من وثائق المنبع الحالية ومن الشيفرة المصدرية، وقد جرى التحقق منها في August 2026.

اقرأ السجل قبل تغيير أي إعداد

أعِد إنتاج المشكلة مع إبقاء نافذة السجل مفتوحة.

cd ./searxng/
docker compose logs -f searxng-core

تصدر رسائل أداة الحد من المعدل من المسجّل المسمّى searx.limiter، وتتضمن عنوان IP. تظهر إصابة قائمة الحظر بصيغة BLOCK 203.0.113.10: matched BLOCKLIST، بينما تظهر إصابة قائمة السماح بصيغة PASS 203.0.113.10: matched PASSLIST. إذا تعذّر على أداة الحد من المعدل الوصول إلى مخزن العدادات، يظهر في السجل The limiter requires Valkey, please consult the documentation، ما يعني أنه لا يجري احتساب أي شيء.

يُسجَّل كل فحص منفرد للروبوتات بمستوى debug، لذلك لن تراه افتراضياً. فعّل debug لاختبار واحد في settings.yml:

general:
  debug: true

يضيف السجل بعد ذلك أسطراً بالصيغة NOT OK (http_accept_language) بجوار شبكة العميل، مع تسمية الفحص الذي فشل. عطّل debug بعد ذلك، لأن الجهة المورّدة توصي بعدم تشغيل نسخة منشورة مع تفعيل debug.

تبدو أعطال المحركات مختلفة تماماً. فهي تذكر محركاً بدلاً من عنوان IP، وأكثرها شيوعاً هو انتهاء المهلة:

HTTP requests timeout (search duration : 3.1 s, timeout: 3.0 s)

توجد صفحة لهذا الغرض أيضاً. عندما تترك enable_metrics على قيمته الافتراضية true، تسجّل نسختك أخطاء المحركات في /stats/errors، ويسرد /preferences المحركات التي تستجيب حالياً. إذا كان /stats/errors ممتلئاً ولم يتضمن السجل أي أسطر searx.limiter، فليست أداة الحد من المعدل هي سبب المشكلة.

نفِّذ تثبيت الإصدار قبل تصحيح أي مشكلة

يتكوّن إعداد الحاوية في المستودع الأصلي من ملفين.

mkdir -p ./searxng/core-config/
cd ./searxng/

curl -fsSL \
    -O https://raw.githubusercontent.com/searxng/searxng/master/container/docker-compose.yml \
    -O https://raw.githubusercontent.com/searxng/searxng/master/container/.env.example

cp -i .env.example .env

يسحب ملف Compose القيمة docker.io/searxng/searxng:${SEARXNG_VERSION:-latest}. ويعني المتغير غير المعيّن القيمة latest، بينما يعني latest أن النسخة ستتغير دون تدخّل منك عند تنفيذ docker compose pull التالي. لذلك قد يتوقف إعداد عمل الأسبوع الماضي عن مطابقة الشيفرة التي تقرؤه. تحمل علامات SearXNG تاريخاً ومعرّف commit. وعلامة المثال في .env.example الأصلي اعتباراً من August 2026 هي 2026.3.25-541c6c3cb، لذلك عيّن قيمة فعلية في .env:

SEARXNG_VERSION=2026.3.25-541c6c3cb

تحقق من العلامات المنشورة وثبّت الإصدار الذي اختبرته فعلياً، ثم صحّح المشكلة باستخدام هدف ثابت. يحتوي ملف .env نفسه على مفتاحك السري، لذلك اقرأ كيفية عمل ملفات البيئة والأسرار في Docker Compose قبل إيداع ذلك المجلد في أي مكان.

يحتاج محدِّد المعدل إلى Valkey، وإلا فلن يعمل

يحصي محدِّد المعدل الطلبات لكل عميل، ويجب مشاركة هذه الإحصاءات بين عمليات العاملين. مخزن البيانات هذا هو Valkey، وهو الفرع الذي تتم صيانته من Redis. تستخدم الأدلة الأقدم لـSearXNG هذا الإعداد باسم redis:. أما الإصدارات الحالية فتقرأ valkey:، لذا انسخ اسم المفتاح من الوثائق الحالية بدلاً من منشور قديم.

use_default_settings: true
server:
  secret_key: "change-this-value"
  limiter: true
  public_instance: false
valkey:
  url: valkey://searxng-valkey:6379/0

يشغّل ملف compose المصدر خدمة searxng-valkey على الصورة docker.io/valkey/valkey:9-alpine، لذلك يُحل اسم المضيف هذا داخل شبكة compose. ويمكن ضبط القيمة نفسها باستخدام متغير البيئة SEARXNG_VALKEY_URL، كما يعمل عنوان URL لمقبس Unix (unix:///path/to/socket.sock?db=0) عندما يشترك SearXNG وValkey في المضيف نفسه.

يعتمد ما يحدث عند غياب المخزن على مفتاح آخر. عند استخدام public_instance: false، يسجّل محدِّد المعدل خطأ Valkey ويتوقف، لذلك يواصل المثيل تقديم الخدمة من دون تحديد للمعدل إطلاقاً. وعند استخدام public_instance: true، تستدعي العملية sys.exit(1) بدلاً من ذلك، لأن المثيل المفتوح مع تعطل حماية الروبوتات يجمع اختبارات CAPTCHA (اختبار تورنغ العام المؤتمت بالكامل للتمييز بين أجهزة الكمبيوتر والبشر) من كل محرك خلال يوم واحد. إذا أعيد تشغيل حاوية في حلقة مباشرة بعد ضبط public_instance: true، فهذه هي الحالة، ويسمّي السطر الأخير قبل كل خروج Valkey.

ما الذي يحسبه المحدِّد فعلياً

ChartSearXNG limiter: requests allowed per client IP, defaults in ip_limit.py
The data behind this chart
[
  {
    "label": "Burst, normal client",
    "max_requests": 15,
    "window": "20 seconds"
  },
  {
    "label": "Burst, flagged client",
    "max_requests": 2,
    "window": "20 seconds"
  },
  {
    "label": "Sustained, normal client",
    "max_requests": 150,
    "window": "10 minutes"
  },
  {
    "label": "Sustained, flagged client",
    "max_requests": 10,
    "window": "10 minutes"
  },
  {
    "label": "Any non-HTML format",
    "max_requests": 4,
    "window": "1 hour"
  },
  {
    "label": "Flagged requests before block",
    "max_requests": 3,
    "window": "30 days"
  }
]

يمكن للعميل العادي إرسال 15 طلباً ضمن نافذة اندفاع مدتها 20 ثانية، و150 طلباً ضمن نافذة مدتها 10 دقائق. عند تصنيف طلب على أنه مشبوه، ينخفض الحد للعميل نفسه إلى 2 ضمن كل نافذة اندفاع. أما الصف الأخير فهو الأشد: بعد 3 طلبات مصنفة كمشبوهة ضمن نافذة مدتها 30 يوماً، يُعاد توجيه ذلك العنوان إلى الصفحة الرئيسية بدلاً من تنفيذ البحث، ويذكر السجل BLOCK: too many request from ... in SUSPICIOUS_IP_WINDOW (redirect to /).

هذه الأرقام ثوابت في searx/botdetection/ip_limit.py. وليست إعدادات، ولا يتيح limiter.toml تعديلها، لذلك يتطلب تغييرها تحرير الشيفرة المصدرية. أما ما يتحكم فيه /etc/searxng/limiter.toml فهو بادئات العناوين المستخدمة لتجميع العملاء، وقائمة الـproxies الموثوقة، والتحقق الاختياري من رمز الرابط، وقائمتا السماح والحظر.

يُصنَّف الطلب على أنه مشبوه بسبب فحوص الرؤوس، ولكل فحص اسم ستراه في سجل التصحيح:

  • http_accept: لا يحتوي الرأس Accept على text/html.
  • http_accept_encoding: لا يذكر الرأس Accept-Encoding gzip ولا deflate.
  • http_accept_language: لا يوجد رأس Accept-Language.
  • http_connection: ضُبط الرأس Connection على close.
  • http_user_agent: User-Agent مفقود أو يطابق نمط bot معروفاً.
  • http_sec_fetch: قيمة الرأس Sec-Fetch-Mode أو Sec-Fetch-Dest ليست القيمة التي يرسلها المتصفح.

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

خلف Reverse Proxy، يحظر محدِّد المعدل الجميع دفعة واحدة

هذه أكثر طريقة شائعة لتعطيل مثيل يعمل بشكل صحيح. يأخذ SearXNG عنوان العميل من أول عنوان IP غير موثوق به في X-Forwarded-For، ثم يستخدم X-Real-IP كبديل، ثم يستخدم عنوان الاتصال الذي فُتح منه الاتصال كبديل أخير. يحدد trusted_proxies في limiter.toml ما إذا كانت هذه الرؤوس موثوقة أصلاً.

إذا لم يكن عنوان الـproxy مدرجاً في تلك القائمة، تُتجاهل الرؤوس ويصل كل زائر بعنوان الـproxy. عندها يشتركون في عدّاد واحد، ولذلك يُحظر الموقع بالكامل عندما يتجاوز المجموع 150 طلباً خلال 10 دقائق. يكفي أن يعيد مستخدم واحد تحميل صفحة النتائج عدة مرات لتعطيل الخدمة للجميع.

الثقة الزائدة أسوأ. إذا أُدرج نطاق عام، يمكن لأي زائر إرسال رأس X-Forwarded-For الخاص به واختيار هوية جديدة لكل طلب، ما يعطّل محدِّد المعدل لأي شخص يعرف كيفية استغلال ذلك. أدرج العنوان الذي يتصل منه الـproxy الخاص بك فقط. في Docker يكون هذا عادةً شبكة bridge داخل 172.16.0.0/12، ويكون هذا السطر معطلاً بالتعليق افتراضياً.

[botdetection]
ipv4_prefix = 32
ipv6_prefix = 48

trusted_proxies = [
  '127.0.0.0/8',
  '::1',
  '172.16.0.0/12',
]

يجب أن يرسل الـproxy الرؤوس أيضاً. لا يضيف Nginx أياً منها تلقائياً:

location / {
    proxy_pass http://127.0.0.1:8080;

    proxy_set_header Host              $host;
    proxy_set_header Connection        $http_connection;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Real-IP         $remote_addr;
    proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
}

يضبط Caddy وTraefik الرؤوس المُعاد توجيهها تلقائياً، لذلك تحتاج معهما إلى تنفيذ الجزء trusted_proxies فقط من الإعداد. تتناول اختيار Reverse Proxy لخدمة مستضافة ذاتياً المفاضلات بين الخيارات. للتحقق من أي من الإعدادين، فعّل debug، وأجرِ بحثاً واحداً من هاتفك عبر بيانات الهاتف المحمول، وتأكد من أن الشبكة في سطر السجل هي عنوان هاتفك وليست عنوان الـproxy.

يحصل وكيلك على أربعة طلبات API في الساعة

يكون إخراج JSON معطّلاً افتراضياً، لذلك يحتاج الوكيل إلى إضافته:

search:
  formats:
    - html
    - json

اقرأ الآن صف المخطط مرة أخرى. يُحتسب كل طلب يطلب تنسيقاً غير HTML ضمن نافذته الخاصة: 4 طلبات لكل 1 hour، لكل عنوان. يستهلك وكيل البحث هذا الحد في مهمة واحدة، ويعيد كل استدعاء بعد ذلك الاستجابة 429. لا يمكن رفع الحد، لأن القيمة موجودة في المصدر.

الحل السليم هو إبلاغ أداة تحديد المعدل بأن هذا العميل معروف. أضف عنوانه إلى قائمة السماح في limiter.toml:

[botdetection.ip_lists]
block_ip = []

pass_ip = [
  '10.8.0.0/24',
]

pass_searxng_org = true

تتقدّم pass_ip على كل طريقة أخرى، لذلك يتجاوز العميل الموجود في قائمة السماح فحوصات الرؤوس أيضاً، ويعمل استدعاء curl مجرّداً. اجعل النطاق أصغر ما يمكن، وفضّل شبكة VPN فرعية أو شبكة حاويات على أي شبكة قابلة للتوجيه. الحل السليم الآخر هو إبقاء الوكيل خارج المسار العام بالكامل: وجّهه إلى عنوان الحاوية على الشبكة الداخلية، حيث لا يرى الـproxy وأداة تحديد المعدل حركة الشبكة. يشرح إتاحة مهارة بحث SearXNG لوكيل ذكاء اصطناعي طريقة إعداد ذلك.

الخيار الذي ينبغي تجنّبه هو توجيه وكيل إلى مثيل عام يشغّله شخص آخر. فهذه أسرع طريقة لحظر عنوان IP الخاص بالمتطوع من محركات البحث upstream، ولهذا يكون تنسيق JSON معطّلاً افتراضياً من الأساس.

عندما تحجبك محركات البحث بدلاً من ذلك

ChartHow long SearXNG suspends an engine, search.suspended_times defaults
The data behind this chart
[
  {
    "label": "SearxEngineTooManyRequests",
    "suspended_seconds": 3600,
    "roughly": "1 hour"
  },
  {
    "label": "SearxEngineAccessDenied",
    "suspended_seconds": 86400,
    "roughly": "1 day"
  },
  {
    "label": "SearxEngineCaptcha",
    "suspended_seconds": 86400,
    "roughly": "1 day"
  },
  {
    "label": "recaptcha_SearxEngineCaptcha",
    "suspended_seconds": 604800,
    "roughly": "7 days"
  },
  {
    "label": "cf_SearxEngineCaptcha",
    "suspended_seconds": 1296000,
    "roughly": "15 days"
  }
]

عندما يجيب أحد المحركات برمز 429 خاص به أو يعرض صفحة CAPTCHA، يرفع SearXNG استثناءً باسم محدد ويتوقف عن إرسال الطلبات إلى ذلك المحرك لفترة من الوقت. تؤدي استجابة «طلبات كثيرة جداً» إلى تعليق المحرك لمدة 3600 ثانية. أما استجابة CAPTCHA عادية أو استجابة رفض الوصول فتعلّق المحرك لمدة 1 day. وتعلّق استجابة CAPTCHA المقدمة عبر Cloudflare المحرك لمدة 15 days، وهي أطول مدة افتراضية في القائمة، لأن هذه الاستجابة تعني أن الحظر موجود عند الحافة، ولن تفيد إعادة المحاولة.

تستخدم حالات الفشل العادية إعدادات مختلفة. يعلّق انتهاء المهلة أو خطأ التحليل المحرك لفترة قصيرة مشتقة من search.ban_time_on_fail، التي تكون قيمتها الافتراضية 5 ثوانٍ، ويحدها search.max_ban_time_on_fail عند 120 ثانية. لذلك يتعافى المحرك البطيء تلقائياً خلال بضع دقائق، بينما يظل المحرك المحظور متوقفاً لساعات. يفسر هذا الفرق عرضاً يصفه المستخدمون بأنه عشوائي: تكون النتائج سليمة، ثم تختفي نتائج أحد المحركات لبقية فترة بعد الظهر.

من الأفضل معالجة حالات انتهاء المهلة قبل إلقاء اللوم على أي طرف. القيمة الافتراضية request_timeout هي 2.0 ثانية، وهي مدة قصيرة لخادم VPS صغير يقع بعيداً عن أقرب خادم edge للمحرك.

outgoing:
  request_timeout: 3.0
  max_request_timeout: 10.0
engines:
  - name: bing
    timeout: 5.0

القيمة request_timeout هي الإعداد الافتراضي لكل محرك، وmax_request_timeout هي الحد الأقصى، ويمكن لمحرك واحد استخدام قيمة timeout الخاصة به. تؤدي زيادة هذه القيم إلى مقايضة زمن تحميل الصفحة بعدد أقل من حالات الفشل، لذلك زدها بمقدار نصف ثانية في كل مرة وراقب /stats/errors بدلاً من القفز مباشرة إلى 10.

إذا كان أحد المحركات يحجب عنوانك فعلاً، فأزله. تنتظر كل عملية بحث أبطأ محرك، لذلك يؤدي إبقاء محرك معلّق دائماً إلى زيادة زمن الاستجابة من دون إرجاع أي نتائج.

use_default_settings:
  engines:
    remove:
      - google

طبّق التغييرات باستخدام docker compose restart searxng-core، ثم نفّذ عدة عمليات بحث وأعد تحميل /stats/errors. تعني الصفحة الفارغة بعد خمس دقائق من الاستخدام الفعلي أن التغيير نجح.

سيُعامَل عنوان IP الخاص بمركز البيانات باعتباره عنواناً آلياً

ينتمي عنوان VPS الخاص بك إلى نطاق استضافة، وتقيّم محركات البحث الكبيرة هذه النطاقات على أنها مصادر للطلبات الآلية. ويعرض بعضها CAPTCHA لكل طلب صادر من مثل هذا العنوان، مهما كانت الرؤوس منضبطة أو كانت وتيرة الطلبات بطيئة. لا يغيّر أي إعداد في settings.yml هذا التقييم.

يمكنك تغيير المحركات التي تطلب منها النتائج، وتحديد ما إذا كان مثيلك مدرجاً للعامة. نادراً ما يتسبب مثيل خاص يستخدمه أفراد منزل واحد في تشغيل آليات الحظر. أما المثيل العام على عنوان استضافة، فسيتعرض لتعليق الحساب في أشد المحركات صرامة. وهذه هي الحالة المعتادة للبرنامج، وليست خللاً في إعداداتك. يستطيع SearXNG توجيه طلبات المحركات عبر وكيل باستخدام outgoing.proxies أو outgoing.using_tor_proxy، ما ينقل حركة الشبكة إلى عنوان مختلف. وتُقيَّم عقد الخروج ومجموعات الوكلاء الرخيصة بدرجة أسوأ من نطاقات الاستضافة، لذلك توقّع أن يؤدي هذا التغيير إلى تراجع النتائج.

راقب المثيل لتكتشف المشكلة أولاً

يستجيب SearXNG على منفذه حتى عند تعليق جميع المحركات، لذلك يظل فحص التوافر الذي يراقب رمز الحالة فقط بحالة سليمة بينما لا يعيد المثيل أي نتائج. افحص المحتوى بدلاً من ذلك: أرسل طلب بحث حقيقياً وتحقق من وجود كلمة تتوقعها في جسم الاستجابة. يوفّر مراقبة الكلمات المفتاحية في Uptime Kuma هذه الوظيفة دون أدوات إضافية. راقب /stats/errors بعد كل ترقية للإصدار أيضاً، لأن المحركات تغيّر HTML الخاص بها، وقد يتعطل المحلل دون أن يكون السبب متعلقاً بحد معدل الطلبات.

FAQ

لماذا يعيد SearXNG الرمز 429 إلى كل زائر بعد وضعه خلف reverse proxy؟

لأن أداة تحديد المعدل تتعامل مع الـproxy باعتباره العميل. لا يقرأ SearXNG X-Forwarded-For إلا عندما يكون عنوان الاتصال مدرجاً في trusted_proxies ضمن /etc/searxng/limiter.toml. إذا لم يكن العنوان مدرجاً، فسيتشارك جميع الزوار عدّاداً واحداً، وسيتجاوزون معاً حد 150 طلباً خلال 10 دقائق. أضف العنوان الذي يتصل منه الـproxy. في Docker يكون هذا عادةً نطاق الجسر 172.16.0.0/12. وتأكد من أن الـproxy يرسل X-Real-IP وX-Forwarded-For. لا تُدرج نطاقاً لا تتحكم فيه، لأن الشبكة الموثوقة تتيح لأي زائر ضبط ذلك الرأس واختيار هوية جديدة لكل طلب.

كم عدد طلبات API التي يسمح بها محدد معدل SearXNG في الساعة؟

أربعة طلبات لكل عنوان IP في الساعة. يُحتسب كل طلب يطلب تنسيقاً غير HTML ضمن نافذة منفصلة مدتها ساعة واحدة. ويُضبط هذا الحد في searx/botdetection/ip_limit.py بدلاً من limiter.toml، لذلك لا يمكن رفعه من ملف الإعداد. سيتجاوزه agent أو script أثناء مهمة واحدة. أضف عنوان العميل إلى pass_ip في limiter.toml، أو صِل إلى المثيل عبر شبكة داخلية لا يرى فيها محدد المعدل الطلب.

لماذا تعود نتائج البحث فارغة من دون ظهور الخطأ 429؟

محركات البحث ترفض خادمك، لا مستخدميك. افتح /stats/errors على المثيل الخاص بك. يسمي هذا الملف كل محرك فشل ويذكر السبب. ويعني ظهور CAPTCHA أو إدخال يفيد برفض الوصول أن المحرك حظر عنوان IP الخاص بخادمك. يعلّق SearXNG المحرك بعد ذلك لمدة ساعة عند تلقي رد يفيد بكثرة الطلبات، ولمدة يوم بعد ظهور CAPTCHA. لا يرفع أي إعداد محلي الحظر المفروض من الجهة upstream. لذلك أزل المحركات التي تحظر عنوانك، وأبقِ على المحركات التي تستجيب.

هل ينبغي تفعيل محدد المعدل على مثيل خاص؟

إذا لم يصل إلى المثيل أحد سواك، فاترك limiter: false. فهو يضيف اعتماداً على Valkey ويحظر scripts الخاصة بك، كما أنه يحمي من حركة مرور غير موجودة لديك. فعّله فور حصول المثيل على عنوان عام، مع public_instance: true. هذا الاقتران مقصود: عند تفعيل public_instance: true من دون Valkey يعمل، تنتهي العملية بحالة 1 بدلاً من التشغيل من دون حماية.