بسیاری از ابزارهای مشاهدهپذیری، پایش و لاگگیری از کدهای پاسخ HTTP بهعنوان شاخصی از کیفیت و آنچه رخ داده استفاده میکنند. اما رویکرد سادهای که فرض میکند کدهای پاسخ ۲۰۰ خوب هستند و کدهای ۴۰۰ یا ۵۰۰ بد هستند، پیچیدگی سیستمهای مدرن را در نظر نمیگیرد.
۲۰۰ همیشه به معنی اوکی بودن نیست
بهطور ساده، کد پاسخ HTTP با وضعیت ۲۰۰ به این معناست که سرور یک درخواست را با موفقیت پردازش کرده است. به همین ترتیب، کدهای ۴۰۰ و ۵۰۰ به این معنا هستند که درخواست با شکست مواجه شده است. در نگاه اول، این رویکرد راهی بسیار ساده برای سازماندهی شاخصها به نظر میرسد؛ بهطوریکه مشخص شود کدام ترافیک خوب است و کدام نیست. اما در واقعیت، در سیستمهای مدرن و حتی سیستمهای قدیمی اوضاع به این سادگی نیست.
آیا میتوان به کدهای 4xx و 5xx اعتماد کرد؟ بله. این کدها معمولاً شاخصهای نسبتاً قابلاعتمادی هستند که نشان میدهند مشکلی رخ داده است، حتی اگر همیشه نوع دقیق مشکل را مشخص نکنند.
مشکل اصلی اینجاست که کدهای ۲۰۰ ممکن است اوکی باشند – یا نباشند. اگر فرض کنید همیشه اوکی هستند، در معرض این خطر قرار میگیرید که تهدیدها و شکستها را کمتر از حد واقعی بشمارید. و میزان این کمشماری میتواند بسیار قابلتوجه باشد.
چرا کدهای پاسخ ۲۰۰ قابلاعتمادتر نیستند؟
بر اساس RFC 9110، کد پاسخ ۲۰۰ (OK) نشان میدهد که درخواست با موفقیت انجام شده و محتوای ارسالشده در پاسخ ۲۰۰ به نوع متد درخواست بستگی دارد. اما اگر کمی جلوتر برویم، از همینجا ابهامهایی درباره معنای دقیق کد ۲۰۰ ظاهر میشود. برای مثال، اگر درخواست از نوع GET باشد، پاسخ حاوی داده خواهد بود، در حالی که در یک درخواست DELETE معمولاً پاسخی وجود ندارد.
با این حال، مشکل بزرگتر واژه «موفقیت» است؛ زیرا این واژه بهطور شفاف تعریف نشده است. هرچه بیشتر به مفهوم موفقیت و حالتهای مختلفی که میتواند با آن همراستا باشد نگاه کنیم، مسئله پیچیدهتر میشود.
از دید استاندارد اولیه، موفقیت اساساً به معنایی است که خود برنامه آن را تعریف میکند؛ تا زمانی که کلاینت و سرور بر سر معنای موفقیت توافق داشته باشند، از منظر استاندارد همهچیز درست است. اما این موضوع باعث میشود که کد ۲۰۰ بیشتر بیانگر قراردادی بین کلاینت و سرور باشد تا یک تعریف سختگیرانه که بتوان از منظر مشاهدهپذیری به آن تکیه کرد.
نگران شدید؟ باید هم بشوید! در یک سازمان با هر اندازهای، شما تعداد زیادی API خواهید داشت. اگر نتوانید تعریف «موفقیت» را در میان آنها نرمالسازی کنید، از نظر مشاهدهپذیری و درک کیفیت این تعاملات با یک مشکل جدی روبهرو خواهید شد.
چه زمانی ۲۰۰ اوکی نیست؟
از همان سال ۱۹۹۳، ارائهدهندگان وبسایت شروع به استفاده از صفحات خطای سفارشی کردند تا به خطاهای ۴۰۴ یا ۵۰۰ با زمینه و راهنمایی بهتر پاسخ دهند. هدف آنها این بود که کاربرانی که با صفحات خطای مدیریتنشده مواجه میشوند، سایت را ترک نکنند.
جایی که اوضاع پیچیده میشود این است که از منظر پایش، این صفحات خطای سفارشی بهصورت ۲۰۰ از طریق شبکه ارسال میشوند. این یعنی پاسخهای ۴۰۰ و ۵۰۰ را، از دید ابزارهای لاگگیری و پایش، به ۲۰۰ تبدیل میکنید. واضح است که این کار دادهها را مخدوش میکند. این پاسخهای ۲۰۰ از منظر پایش و شاخصها قطعاً اوکی نیستند.
در دنیای وب، APIهای عمومی که امروز میشناسیم بر پایه استانداردهای وب ساخته شدهاند. و درست مانند وبسایتهایی که این APIها جایگزین آنها شدهاند، توسعهدهندگان میتوانند از کدهای HTTP هرطور که بخواهند استفاده کنند. آنها خودشان موفقیت را در قالب کدهای پاسخ تعریف میکنند؛ هیچ روش مرجع و یکتایی برای این کار وجود ندارد.
حتی فریمورکهای وب زیربنایی نیز ممکن است رفتارهای پیشفرض متفاوتی در استفاده از کدهای پاسخ و رفتار مناسب کلاینت داشته باشند. برای مثال، GraphQL بهصورت قراردادی فقط کدهای پاسخ ۲۰۰ بازمیگرداند. کدهای پاسخ برای سیگنالدهی خطا استفاده نمیشوند؛ بلکه خطاها در payload قرار میگیرند. با این حال، این رفتار همچنان با زبان و نیت اصلی استاندارد RFC 9110 سازگار است.
نکته مهم دیگر این است که APIها نسبت به وبسایتها از کدهای پاسخ بیشتری استفاده میکنند. وبسایتها معمولاً به ۲۰۰، ۴۰۰، ۴۰۴ و ۵۰۰ محدود میشوند. اما APIها ممکن است کدهای ۲۰۱ یا ۲۰۴ هم برگردانند. تنوع بسیار بیشتری در رفتار APIهای REST و سیگنالهایی که ارسال میکنند وجود دارد. علاوه بر این، استفاده نادرست از کدهای HTTP بهسختی قابل اصلاح است؛ زیرا شما فقط رفتار سرور را تغییر نمیدهید، بلکه منطق تمام کلاینتها را هم عوض میکنید – یعنی در واقع قرارداد بین کلاینت و سرور را تغییر میدهید.
تمام این موارد به این معناست که نمیتوانید انتظار داشته باشید استفاده از کدهای وضعیت HTTP از منظر پایش کاملاً سازگار و یکنواخت باشد. و اگر نتوانید به سازگاری کامل تکیه کنید، به یک پشته پایش و مشاهدهپذیری نیاز دارید که به شما کمک کند این نگاشت را انجام دهید تا شکستها و تهدیدها کمتر از حد واقعی شمرده نشوند.
نمونههایی از شرایطی که ۲۰۰ اوکی نیست
با در نظر گرفتن مثال زیر، بهراحتی میتوان دید که چگونه شکستها کمتر از حد واقعی شمارش میشوند: یک پاسخ ۲۰۰ که در آن پردازش سمت بکاند با شکست مواجه شده است. این شکست میتواند در خود API یا در یک API بالادستی که به آن وابسته است رخ دهد. مصرفکننده یک stack trace دریافت میکند که توسط یک error handler گرفته نشده است. این نهتنها یک شکست است، بلکه میتواند منجر به نشت اطلاعات هم بشود. با این حال، از منظر پروتکل، پاسخ ۲۰۰ (OK) است.

نمونه کلاسیک دیگر از شرایطی که ۲۰۰ اوکی نیست، زمانی است که در لایه برنامه در مقایسه با لایه شبکه، اختلال جدی در ارتباط وجود دارد. از منظر شبکه، درخواست دریافت شده و پاسخ ارسال شده است. اما آن پاسخ میتواند JSON نامعتبر باشد.

از یک ویرگول در جای اشتباه گرفته تا یک کاراکتر escapeنشده، هرکدام میتوانند باعث این مشکل شوند و هیچ کلاینتی بدون دریافت خطا از parser JSON خود قادر به پردازش آن نخواهد بود. این موضوع آسیبهای زنجیرهای ایجاد میکند – اما همچنان از منظر عمودی، پاسخ ۲۰۰ گزارش میشود. چون در نهایت پاسخی ارسال شده است. این وضعیت اوکی نیست.
حالا تصور کنید یک مهاجم درخواست یک فایل محدودشده، مانند فایل .env، را ارسال کند. اگر API آن فایل را به مهاجم بدهد، از منظر پروتکل میتواند پاسخ ۲۰۰ (OK) داشته باشد. اما از منظر امنیتی، این اصلاً قابلقبول نیست.

نمونه دیگر از ۲۰۰های نادرست، GraphQL و برنامههای سفارشی یا داخلی هستند که بهصورت قراردادی فقط از ۲۰۰ استفاده میکنند.
چگونه درک بهتری از وضعیت پاسخها به دست آوریم
اگر میخواهید به وضعیت ایدهآل در درک پاسخها برسید، باید فراتر از کدهای پاسخ نگاه کنید. البته میتوانید از آنها بهعنوان نقطه شروع استفاده کنید؛ بالاخره هر داده پایشی بهتر از هیچ است.
پس از آن، زمان آن میرسد که مفهوم اختصاصی خودتان از «وضعیت پاسخ» را تعریف کنید؛ مفهومی که بتوانید بهجای جاهایی که معمولاً وسوسه میشوید از کد پاسخ استفاده کنید، به کار ببرید. با این کار میتوانید معنای واقعی موفقیت را وارد معادله کنید و آن را مطابق انتظاری که استاندارد داشته، روی دادهها اعمال نمایید.
گام بعدی، محاسبه این وضعیت پاسخ بر اساس کل درخواست و پاسخ در کنار یکدیگر است. وقتی این را با اطلاعات تهدید، و دانش دقیق از اینکه مشخصات API واقعاً چه کاری انجام میدهند ترکیب کنید، به مفهومی واقعاً قابلاعتماد از وضعیت پاسخ میرسید – مفهومی که مستقل از محدودیتهای کدهای پاسخ است و در برابر کمشماری تهدیدها و شکستها مقاومتر عمل میکند.
