در این مطلب به بررسی cloudflare outage در تاریخ ۵ دسامبر ۲۰۲۵ و تأثیر آن بر خدمات وب می‌پردازیم و خلاصه‌ای از اتفاقات کلیدی و اقدامات مدیریتی پس از رخداد ارائه می‌کنیم.


کلید واژه کلیدی این بررسی cloudflare outage است؛ قصد دارد نگاهی به عوامل فنی، شیوه‌های پاسخ‌دهی و نحوه پیشگیری از وقفه‌های مشابه بیندازد.

⚠️ صبح روز 5 دسامبر 2025 ساعت 08:47 UTC، بخشی از شبکه Cloudflare دچار اختلال شد که تا حدود 09:12 ادامه پیدا کرد (تقریباً 25 دقیقه). در این بازه زمانی زیرمجموعه‌ای از مشتریان—معادل تقریباً 28٪ از ترافیک HTTP که از طریق Cloudflare سرو می‌شد—با خطای HTTP 500 مواجه شدند. شرایط لازم برای اینکه یک مشتری خاص تحت تأثیر قرار بگیرد، ترکیبی از وضعیت‌های شبکه و پیکربندی بود.

🔍 این اختلال ناشی از حمله سایبری یا فعالیت مخرب نبود. عامل شروع‌کننده تغییراتی در منطق body parsing بود که در تلاش برای شناسایی و کاهش آسیب‌پذیری سطح صنعت معرفی‌شده این هفته برای React Server Components (CVE-2025-55182) اعمال شد.

📈 برای تحلیل: Cloudflare به‌طور معمول برای محافظت در برابر payloadهای مخرب از WAF استفاده می‌کند که درخواست‌های HTTP را با بافر کردن body در حافظه تحلیل می‌کند. تا پیش از این بافر روی 128KB تنظیم شده بود؛ برای محافظت از برنامه‌هایی که از Next.js استفاده می‌کنند، تصمیم گرفته شد بافر را به 1MB افزایش دهند تا بیشترین تعداد مشتریان پوشش داده شوند.

🛠️ این تغییر ابتدا از طریق سیستم gradual deployment (استقرار تدریجی) منتشر شد. طی rollout متوجه شدند ابزار داخلی تست WAF از بافر بزرگ‌تر پشتیبانی نمی‌کند. از آنجایی که آن ابزار تست داخلی در آن لحظه ترافیک مشتریان را تحت تأثیر قرار نمی‌داد، تصمیم گرفته شد آن را غیرفعال کنند.

⚠️ نکته مهم: غیرفعال‌سازی ابزار تست داخلی از طریق global configuration system انجام شد. این سیستم انتشار را به‌صورت تدریجی انجام نمی‌دهد و تغییرات را در عرض چند ثانیه به کل ناوگان سرورها منتقل می‌کند. متأسفانه در نسخه قدیمی پروکسی (FL1)، در شرایط خاص، غیرفعال‌سازی این ابزار باعث به‌وجود آمدن یک حالت خطا شد که نتیجه‌اش سرو شدن کدهای HTTP 500 از شبکه بود.

[lua] Failed to run module rulesets callback late_routing: /usr/local/nginx-fl/lua/modules/init.lua:314: attempt to index field 'execute' (a nil value)

🧩 به‌محض انتشار تغییر، اجرای کد در پروکسی FL1 به باگی در ماژول rules برخورد کرد که منجر به این استثنای Lua شد و در نتیجه HTTP 500 صادر شد. مشکل خیلی زود شناسایی و در 09:12 تغییر بازگردانده شد و پس از آن ترافیک به حالت عادی برگشت.

📌 مشتریانی که تحت تأثیر قرار گرفتند، شرایط زیر را داشتند: دارا بودن پروکسی قدیمی FL1 و فعال بودن Cloudflare Managed Ruleset. در این حالت، تقریباً تمام درخواست‌های سایت‌ها پاسخ HTTP 500 دریافت کردند (به جز چند endpoint تستی مثل /cdn-cgi/trace). مشتریانی که این تنظیمات را نداشتند و همچنین ترافیک شبکه چین، متأثر نشدند.

⚙️ توضیح فنی بیشتر درباره runtime error: سیستم rulesets شامل مجموعه‌ای از قوانین است که برای هر درخواست ارزیابی می‌شوند. هر rule شامل یک filter و یک action است؛ actions معمولاً مثل “block”، “log” یا “skip” هستند. نوع دیگری از action با نام “execute” وجود دارد که باعث می‌شود ruleset دیگری اجرا شود—از این مکانیزم برای ارزیابی قوانین جدید قبل از انتشار عمومی استفاده می‌شود. در این حادثه تلاش می‌شد تا rulesetهای تستی غیرفعال شوند.

🔒 سیستم killswitch برای غیرفعال‌سازی سریع یک rule طراحی شده است و اطلاعاتش را از global configuration می‌گیرد. SOP مربوطه در این رخداد دنبال شد، اما تا کنون هیچ‌وقت killswitch روی یک rule با action=”execute” اعمال نشده بود. وقتی killswitch اعمال شد، کد به‌درستی evaluation اجرای action “execute” را رد کرد و ruleset داخلی اجرا نشد، اما بعد در پردازش نتایج کلی یک خطا رخ داد:

if rule_result.action == "execute" then
  rule_result.execute.results = ruleset_results[tonumber(rule_result.execute.results_index)]
end

💡 مشکل این بود که کد انتظار داشت که اگر action=”execute” است، شیء rule_result.execute همیشه موجود باشد؛ اما چون آن rule به‌طور مؤثری skip شده بود، این شیء وجود نداشت و تلاش برای دسترسی به فیلد آن باعث خطای nil lookup در Lua شد. این یک خطای ساده در منطق کد است که سال‌ها بدون کشف باقی مانده بود. در نسخه replacement پروکسی (FL2) که با Rust بازنویسی شده، این خطا رخ نداد—زبان‌های با strong type سیستم چنین خطاهایی را بهتر جلوگیری می‌کنند.

🔁 ارتباط با حادثه 18 نوامبر: دو هفته قبل نیز یک تغییر نامرتبط باعث یک حادثه طولانی‌تر شد که ناشی از انتشار سریع یک deployment برای کاهش ریسک امنیتی بود و تقریباً تمام مشتریان را تحت تأثیر قرار داد. پس از آن با صدها مشتری صحبت شده و برنامه‌هایی برای جلوگیری از انتشارهای تک که می‌توانند widespread impact داشته باشند، تدوین شده‌اند. این تغییرات احتمالاً می‌توانستند مانع از اثر امروز شوند اما هنوز کامل پیاده نشده‌اند.

📌 پروژه‌های اصلی که در اولویت قرار دارند تا جلوی تکرار این اتفاق‌ها گرفته شود:
– Enhanced Rollouts & Versioning: داده‌هایی که برای rapid threat response و پیکربندی عمومی استفاده می‌شوند باید مثل نرم‌افزار با health validation و قابلیت rollback آهسته منتشر شوند.
– Streamlined break glass capabilities: اطمینان از اینکه عملیات بحرانی حتی در مواجهه با انواع جدید خطاها نیز قابل انجام باشند—برای سرویس‌های داخلی و روش‌های استاندارد تعامل با control plane.
– “Fail-Open” Error Handling: جایگزینی منطق hard-fail که به‌صورت نادرست اعمال شده است؛ در سناریوهایی که فایل پیکربندی خراب یا خارج از رنج است، سیستم باید خطا را لاگ کند و یا به حالت known-good یا عبور ترافیک بدون scoring برگردد، به‌جای اینکه درخواست‌ها را بی‌قید و شرط رها کند. بعضی سرویس‌ها احتمالاً گزینه fail open/closed را به مشتری خواهند داد.

🗓️ قبل از پایان هفته آینده یک breakdown دقیق از پروژه‌های افزایش resiliency منتشر خواهد شد. تا زمانی که این کار کامل نشود، تغییرات روی شبکه محدود خواهند شد تا اطمینان از داشتن مکانیزم‌های rollback و mitigation بهتر حاصل شود.

🙏 این اتفاق‌ها و هم‌پوشانی‌شان برای شبکه‌ای در مقیاس Cloudflare قابل قبول نیست. تیم از بابت اختلال و ناراحتی ایجادشده عذرخواهی می‌کند و اولویت اصلی‌شان جلوگیری از تکرار چنین وقایعی است.

⏱️ زمان‌بندی مختصر رخداد (UTC):

08:47 — INCIDENT start: Configuration change منتشر و به شبکه propagate شد.

08:48 — Full impact: تغییر به‌طور کامل منتشر شد و تأثیر کامل شروع شد.

08:50 — INCIDENT declared: سیستم‌های خودکار هشدار صادر کردند.

09:11 — Change reverted: تغییر بازگردانده شد و شروع به انتشار revert شد.

09:12 — INCIDENT end: بازگشت کامل منتشر شد و تمام ترافیک بازیابی شد.

🚀 برای جمع‌بندی: ریشه اختلال ترکیب سه عامل بود — افزایش buffer برای محافظت از مشتریان React، غیرفعال‌سازی ابزار تست داخلی از طریق سیستمی که انتشار سریع انجام می‌دهد، و یک باگ قدیمی در FL1 که هنگام skip کردن یک rule با action=”execute” باعث nil access در Lua می‌شد. کارهایی در جریان است تا این مدل تغییرات سریع و اثرات پرتلفات‌شان کنترل و ایمن شوند. اگر در حوزه DevOps/SRE کار می‌کنید و دنبال جزئیات بیشتر فنی هستید، منطقی است روی مواردی مانند staged rollouts، feature flagging، و fail-open semantics در طراحی‌های خود تمرکز کنید—این‌ها دقیقاً همان نقاطی هستند که باعث ایجاد یا جلوگیری از چنین اختلالاتی می‌شوند. 😊