Course-এর সূচি

2.2

HTTP Method ও Status Code

কোন method safe, কোনটা idempotent, কোনটা cache করা যায়, আর প্রতিটা status code পরিবার client-কে কী জানায়।

50 মিনিট পড়া + Animation

টিপস: ডটেড-আন্ডারলাইন করা যেকোনো শব্দে ট্যাপ বা Hover করলে তার অর্থ দেখবে।

এই Lesson শেষে তুমি পারবে

  • প্রতিটা HTTP Method-এর ঠিক মানে বলতে (GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, CONNECT, TRACE, আর নতুন QUERY), আর কোনটায় Body থাকে তা জানতে।
  • প্রতিটা Method Safe কি না, Idempotent কি না, Cacheable কি না তা বলতে, আর Browser, Proxy, Cache আর Retry Logic-এর কাছে এই তিনটা গুণ কেন জরুরি তা বোঝাতে।
  • PUT আর PATCH-এর মধ্যে, আর JSON Merge Patch আর JSON Patch-এর মধ্যে বেছে নিতে; If-Match দিয়ে হারানো Update ঠেকাতে।
  • Timeout-এর পরে POST Retry করলে কেন Customer-এর টাকা দুবার কাটতে পারে, আর Idempotency Key কীভাবে সেটা ঠেকায় তা বোঝাতে।
  • একজন অভিজ্ঞ Engineer-এর মতো Status Code পড়তে: পাঁচটা পরিবার, যে Code-গুলো সত্যিই দরকারি (100 থেকে 504), কোন Redirect Method রেখে দেয়, 401 বনাম 403, 404 বনাম 410, আর 502, 503 বা 504 আসলে কে বানাল।
  • কোন Response Retry করা নিরাপদ তা ঠিক করতে, আর “ভেতরে Error, বাইরে 200”-এর বদলে Problem Details (RFC 9457) দিয়ে Error Response বানাতে।

#এই Lesson কোথায় বসে

Lesson ১.১-এ আমরা Request–Response চক্র দেখেছি, Method-এর একটা Table, রোজ দেখা Status Code-গুলো, আর একটা নিয়ম: “Retry করো শুধু Idempotent Request”। এবার ওই দুটো Table খুলে ভেতরটা দেখব। প্রতিটা সারির পেছনে লুকিয়ে আছে এমন একটা সিদ্ধান্ত, যেটা কোনো না কোনো Software নেয় তোমার Application না বুঝেই: Browser ভাবছে একটা Link আগেভাগে আনবে কি না, Proxy ভাবছে একটা Request আরেক Server-এ আবার পাঠাবে কি না, CDN ভাবছে একটা Response জমা রাখবে কি না, Client Library ভাবছে একটা ব্যর্থতা Retry করার মতো কি না। এরা সবাই সিদ্ধান্ত নেয় শুধু Method আর Status Code দেখে।

তাই Method আর Status Code হলো পথের প্রতিটা মেশিনের সাথে একটা চুক্তি, শুধু তোমার API Docs যে মানুষটা পড়ে তার সাথে না। ঠিকভাবে ব্যবহার করলে Caching, Retry আর Monitoring বিনা খরচে কাজ করে। আলগাভাবে ব্যবহার করলে পাবে দুবার Payment, Cache-এ জমে থাকা Error Page, আর বিভ্রাটের মাঝেও সবুজ Dashboard।

আরও দুটো Lesson এর ওপর দাঁড়িয়ে: ২.৩-এ আছে Header (OPTIONS দিয়ে চলা CORS Preflight সহ), আর ২.৪-এ Caching আর Validation (ETag আর 304) বিস্তারিত। এখানে HTTP-র Version কোনো ব্যাপার না: HTTP/1.1, HTTP/2 আর HTTP/3 (Lesson ২.১) হুবহু একই Method আর Status Code বয়ে নেয়। এদের মানে একবারই লেখা আছে, RFC 9110, HTTP Semantics-এ (২০২২)।

#Method: Side Effect নিয়ে একটা প্রতিশ্রুতি

প্রতিটা Request-এর প্রথম শব্দটাই Method (কেউ কেউ Verb-ও বলে)। এটা Server-কে বলে URL-এ যে Resource-এর নাম, তা নিয়ে Client কী করাতে চায়। Method Case-sensitive, আর রীতি অনুযায়ী বড় হাতের অক্ষরে লেখা হয়।

Methodকী চায়Request Bodyসাধারণ সফল উত্তর
GETএই Resource-এর বর্তমান রূপটা আমাকে দাওকোনো নির্দিষ্ট মানে নেই200 (Range হলে 206, না বদলালে 304)
HEADGET-এর মতোই, কিন্তু শুধু Header পাঠাওনা200
POSTএই Body-টা Resource নিজের নিয়মে প্রক্রিয়া করুকহ্যাঁ201 + Location (তৈরি হলে), 200 (ফলাফল), 202 (Queue-তে), 204
PUTএই URL-এর Resource-টা ঠিক এটাই বানাও (তৈরি বা প্রতিস্থাপন)হ্যাঁ, পুরো রূপটা201 (তৈরি), 200 বা 204 (বদলানো)
PATCHResource-এ এই পরিবর্তনগুলো বসাওহ্যাঁ, একটা Patch Document200 বা 204
DELETEএই URL-এর Resource সরিয়ে দাওকোনো নির্দিষ্ট মানে নেই204, 200, বা 202 (মোছা পরে হবে)
OPTIONSএই Resource নিয়ে আমি কী কী করতে পারি?কদাচিৎ204 বা 200, সাথে Allow Header
CONNECTProxy, এই Host আর Port-এ একটা কাঁচা Tunnel খোলোনা200, তারপর কাঁচা Byte
TRACEআমার Request-টা ফেরত পাঠাও, দেখি কী পৌঁছালনা (থাকা চলবে না)200, সাধারণত বন্ধ

#প্রতিটা Method খুঁটিয়ে

GET একটা Resource-এর রূপ এনে দেয়। Web-এ সবচেয়ে বেশি এটাই চলে, আর বেশিরভাগ Cache শুধু এটাই জমা রাখে। GET শর্তযুক্ত হতে পারে (If-None-Match, উত্তর 304 Not Modified, Lesson ২.৪), আবার Range Request হতে পারে (Range: bytes=0-1023, উত্তর 206 Partial Content; Video টেনে সামনে যাওয়া আর থেমে যাওয়া Download আবার শুরু করা এভাবেই হয়)। নোটের Table বলে GET-এ Body থাকে না; আরও ঠিকভাবে বললে, GET-এ Body নিষিদ্ধ না, কিন্তু তার কোনো নির্দিষ্ট মানে নেই: অনেক Server, Proxy আর CDN সেটা উপেক্ষা করে বা ফেলে দেয়, কেউ কেউ পুরো Request-ই বাতিল করে। জরুরি Data কখনো GET-এর Body-তে রেখো না।

HEAD হলো Body ছাড়া GET। Header-গুলো GET যা দিত তা-ই, Content-Length, Last-Modified আর ETag সহ। কিছু আছে কি না, Download-এর আগে File কত বড়, কিংবা একটা Link এখনো বেঁচে আছে কি না, এসব জানতে এটা কাজে লাগে, পুরো Byte না টেনেই।

POST হলো সব কাজের Method। নোটে একে বলা হয়েছে “তৈরি করা”, আর একটা Collection-এ নতুন Item বানানোই (POST /orders) এর সবচেয়ে চেনা ব্যবহার। কিন্তু RFC 9110 আরও বড় করে বলে: Target Resource পাঠানো Body-টা নিজের নিয়মে প্রক্রিয়া করে। Form জমা দেওয়া, একটা Comment যোগ করা, একটা Job শুরু করা (POST /reports), একটা কাজ চালানো (POST /orders/77/cancel), আর অন্য কোনো Method-এ যা খাপ খায় না, সবই POST। কিছু তৈরি হলে উত্তর দাও 201 Created, সাথে নতুন Resource-এর ঠিকানা দেওয়া একটা Location Header।

PUT URL-এর Resource-এর পুরো অবস্থা Body দিয়ে বদলে দেয়, আর না থাকলে ওখানেই তৈরি করে। নোটে PUT-এর বর্ণনা দুবার আছে: একবার “বদলানো বা Update”, আরেকবার Protocol অংশে “তৈরি বা পুরো Update”। দ্বিতীয়টাই পূর্ণ সংজ্ঞা। POST-এর সাথে আসল পার্থক্য হলো URL কে বাছে: POST /users-এ নতুন ID বাছে Server; PUT /users/42 বা PUT /files/report.pdf-এ নামটা দেয় Client। PUT নতুন কিছু তৈরি করলে উত্তর 201, আগেরটা বদলালে 200 বা 204।

PATCH (২০১০ সালে RFC 5789 যোগ করেছে) আংশিক পরিবর্তন করে। Body হলো একটা Patch Document, আর তার Media Type বলে দেয় কীভাবে প্রয়োগ করতে হবে। তাই একই URL আলাদা আলাদা Patch Format নিতে পারে (পরের অংশে)।

DELETE Resource সরিয়ে দেয়, আরও ঠিকভাবে বললে URL আর তার জিনিসটার সম্পর্ক কেটে দেয়। এটা Idempotent: দুবার মুছলেও শেষ অবস্থা একই। GET-এর মতোই, DELETE-এর Body-র কোনো নির্দিষ্ট মানে নেই। মোছাটা যদি পরে হয় (পেছনে চলা একটা Purge), উত্তর দাও 202 Accepted।

OPTIONS জিজ্ঞেস করে একটা URL-এর জন্য Server কী কী সমর্থন করে। উত্তরে Allow Header-এ Method-এর তালিকা থাকে। যেসব Cross-origin Request “Simple” না, সেগুলোর আগে Browser নিজে থেকেই এটা পাঠায়, এর নাম CORS Preflight (Lesson ২.৩)। OPTIONS * পুরো Server-টার কথা জিজ্ঞেস করে।

CONNECT একটা Proxy-কে TCP Tunnel খুলতে বলে: CONNECT api.example.com:443। 200 পাওয়ার পরে Proxy শুধু দুই দিকে Byte পার করে, আর Client Tunnel-এর ভেতর দিয়ে TLS চালায়, তাই Proxy ভেতরের কিছুই দেখে না। Office-এর Forward Proxy পেরিয়ে HTTPS এভাবেই যায় (Lesson ১.১২)। HTTP/2 আর HTTP/3-এ একটা Extended CONNECT-ও আছে, যা দিয়ে এদের ওপর WebSocket চালানো হয় (Lesson ২.৬)।

TRACE শেষ Server-কে বলে, যে Request সে পেয়েছে তা হুবহু ফেরত পাঠাও, যাতে বোঝা যায় পথে Proxy-রা কী বদলেছে। কিন্তু ফেরত আসা জিনিসে Cookie আর Authorization Header থাকতে পারে (একসময় “Cross-site Tracing” নামের Attack এভাবেই সেগুলো চুরি করত), তাই Server আর Proxy এটা বন্ধ রাখে। তুমি এটা দেখবে শুধু Security Scan-এ, যেখানে চালু থাকলে সেটাকে সমস্যা হিসেবে দেখায়।

QUERY নতুন: IETF-এর HTTP Working Group এমন একটা Method Standard করছে যেটা GET-এর মতোই Safe আর Idempotent, কিন্তু Query বয়ে নেয় Body-তে। এটা সেই Search-এর জন্য যেগুলো URL-এ আঁটে না বা খুব জটিল (৫০টা শর্তের Filter, একটা GraphQL Query, একটা Vector Search)। আজ এসব Search POST দিয়ে চলে, আর তাতে GET-এর Caching আর Retry-র সুবিধা হারায়। ব্যবহারের আগে দেখে নাও এটা RFC হিসেবে প্রকাশিত হয়েছে কি না, আর তোমার Framework, Proxy আর CDN এটা চেনে কি না। আরও কিছু Extension আছে: WebDAV File সম্পাদনার জন্য PROPFIND, MKCOL, LOCK ইত্যাদি যোগ করেছিল, যেগুলো এখনো কিছু File Server-এ দেখবে।

#Safe, Idempotent, Cacheable: তিনটা আলাদা গুণ

নোটে দুটো গুণের কথা আছে; HTTP-তে আছে তিনটা, আর প্রতিটা আলাদা আলাদা মেশিন ব্যবহার করে।

  • Safe: Client Server-এর অবস্থা বদলাতে চায় না, বদলাবে বলে আশাও করে না। Safe হলো Request-এর মানে নিয়ে, Server একদম কিছু করবে না এমন না: Log লেখা, Page View গোনা, Cache গরম করা চলে। Safe Request সংজ্ঞাতেই নিরীহ, তাই Browser আগেভাগে আনে, Crawler আর Link Preview নির্দ্বিধায় অনুসরণ করে, আর পাঠানোর আগে কেউ User-কে জিজ্ঞেস করে না।
  • Idempotent: একই Request কয়েকবার পাঠালে Server-এর ওপর উদ্দেশ্যমূলক প্রভাব একবার পাঠানোর মতোই। Response আলাদা হতে পারে (DELETE প্রথমে 204, পরে 404)। প্রতিটা Safe Method Idempotent; DELETE আর PUT Safe না হয়েও Idempotent। পুনরাবৃত্তি নিরীহ বলে, উত্তর আসার আগে Connection ভাঙলে Client, Proxy আর Library নিজে থেকেই Idempotent Request আবার পাঠাতে পারে।
  • Cacheable: এই Method-এর Response জমা রেখে পরের Request-এ আবার ব্যবহার করা যায়। GET আর HEAD-এর Response Cacheable। POST-এর Response Cacheable শুধু তখন, যখন স্পষ্ট Freshness তথ্য আর URL-এর সাথে মেলা Content-Location থাকে, যা প্রায় কোনো Cache-ই বাস্তবে করে না। সফল PUT, PATCH, POST বা DELETE ওই URL-এর জন্য Cache-এ যা ছিল তা বাতিল করে দেয়।
MethodSafeIdempotentCacheableকেন
GET✅✅✅শুধু পড়ে; Response হলো Resource-এর একটা কপি
HEAD✅✅✅Body ছাড়া GET
OPTIONS✅✅❌শুধু সক্ষমতা জিজ্ঞেস করে; Spec বলে এর Response Cacheable না (CORS Preflight-এর নিজের Access-Control-Max-Age Cache আছে)
TRACE✅✅❌শুধু ফেরত পাঠায়
PUT❌✅❌শেষ অবস্থা বসিয়ে দেয়; আবার করলে একই অবস্থা বসে
DELETE❌✅❌একবার মুছলে নেই; দুবার মুছলেও নেই
POST❌❌কদাচিৎমানে Resource-এর ওপর নির্ভর করে; আবার করলে দ্বিতীয় Order তৈরি হতে পারে
PATCH❌নিশ্চিত না❌Patch-এর ওপর নির্ভর: “city Sylhet করো” বারবার করা যায়, “Item ৭ যোগ করো” যায় না
CONNECT❌❌❌প্রতিবার নতুন Tunnel খোলে
QUERY (খসড়া)✅✅✅Body সহ GET-এর মানে; Cache Key-তে Body-ও ধরতে হয়

এখান থেকে সরাসরি দুটো কথা আসে। এক, Safe Method-কে Safe রাখার দায় Server-এর: একটা GET /posts/7/delete Link আজ হোক কাল হোক, কোনো Prefetcher বা Crawler অনুসরণ করবেই। দুই, যে API সবকিছু POST দিয়ে পাঠায় (RPC ধাঁচের “Tunnel”), সে প্রতিটা Read-এর Caching আর স্বয়ংক্রিয় Retry ফেলে দেয়।

আবার পাঠাও: একই Method বারবার পাঠালে Server-এর অবস্থা কী হয়

Idempotent মানে একবার পাঠাও বা দশবার, Server-এর ওপর প্রভাব একই। Response না, Database-এর ওপরের অবস্থাটা খেয়াল করো।

পরিস্থিতি

একটা JSON Merge Patch যেটা একটা মান বসায়। সংজ্ঞা অনুযায়ী PATCH Idempotent না, কিন্তু এই Patch-টা Idempotent-এর মতোই আচরণ করে।

ClientApp, Crawler বা Retry Library
API Serverapi.example.com
Databasecity = Dhaka
PATCH /users/42 {"city": "Sylhet"}
ধাপ 1 / 6
1

Body-টা একটা Merge Patch (Content-Type: application/merge-patch+json): “city-কে Sylhet বানাও”। এটা শেষ মানটা বলে দেয়, বর্তমান মানের তুলনায় কতটা বদলাতে হবে সেটা না।

নিজেকে যাচাই করোপ্রশ্ন

একটা Team GET /cart/add?item=7 খুলে দিল, যাতে Product-এর Link-এ এক ক্লিকেই Cart-এ যোগ হয়। শেষমেশ কী গোলমাল হবে?

#PUT বনাম PATCH

PUT বলে “ঠিক এটাই বানাও”। যে Field বাদ দেবে সেটা “যেমন ছিল তেমন” থাকে না, মুছে যায় (বা Default-এ ফিরে যায়)। এটা Idempotent ঠিক এই কারণেই যে ফলাফল পুরোনো অবস্থার ওপর নির্ভর করে না। Client-এর হাতে পুরো Resource থাকলে PUT নাও: একটা Settings Document, একটা File, একটা Configuration Object।

PATCH বলে “এই পরিবর্তনগুলো বসাও”। পরিবর্তনগুলো কীভাবে পড়তে হবে তা নির্ভর করে Body-র Content-Type-এর ওপর, আর JSON-এর জন্য দুটো Standard Format আছে:

JSON Merge Patch (RFC 7396, application/merge-patch+json): Body দেখতে Resource-এর মতোই, শুধু যে Field বদলাবে সেগুলো থাকে। null মানে “এই Field মুছে দাও”।

PATCH /users/42 HTTP/1.1
Content-Type: application/merge-patch+json

{ "city": "Khulna", "phone": null }

এটা সহজ, আর সাধারণ ব্যবহারে Idempotent। সীমাবদ্ধতা: কোনো Field-এ null মান বসানো যায় না, আর Array পুরোটাই বদলায় (“একটা Tag যোগ করো” বলা যায় না)।

JSON Patch (RFC 6902, application/json-patch+json): Body হলো Operation-এর একটা তালিকা (add, remove, replace, move, copy, test), সাথে JSON Pointer Path। পুরো তালিকা Atomically প্রয়োগ হয়: একটা Operation ব্যর্থ হলে কিছুই বদলায় না।

PATCH /carts/9 HTTP/1.1
Content-Type: application/json-patch+json

[
  { "op": "test",    "path": "/version",  "value": 7 },
  { "op": "add",     "path": "/items/-",  "value": { "sku": "A-17", "qty": 1 } },
  { "op": "replace", "path": "/coupon",   "value": "EID25" }
]

এ দিয়ে সবই বলা যায় (Array-তে ঢোকানো, সরানো, পাহারাদার হিসেবে test), কিন্তু /items/--এ (“শেষ Element-এর পরে”) add Idempotent না: দুটো কপি দুটো Item যোগ করে। test Operation হলো ভেতরেই রাখা একটা নিরাপত্তা যাচাই: version যদি আর ৭ না থাকে, পুরো Patch ব্যর্থ।

Server কোন কোন Patch Format নেয় তা Accept-Patch Header-এ জানায়, আর অন্য কিছু এলে উত্তর দেয় 415 Unsupported Media Type। বর্তমান অবস্থার ওপর যে Patch বসানো যায় না, তার উত্তর 409 Conflict (বা 422)।

PUTJSON Merge PatchJSON Patch
Bodyপুরো Resourceশুধু বদলানো FieldOperation-এর তালিকা
Idempotentহ্যাঁবাস্তবে হ্যাঁসাধারণভাবে না
Field মোছাবাদ দিয়ে দাও"field": null{"op": "remove"}
Array-র ভেতরে বদলপুরো Array পাঠাওপুরো Array পাঠাওহ্যাঁ (Index ধরে add, remove)
কোথায় ভালোপুরো Document, Fileসাধারণ Form Editনিখুঁত বা শর্তযুক্ত Edit

#হারানো Update আর শর্তযুক্ত Request

দুজন মানুষ একই Record সম্পাদনা করলে, Method একদম ঠিক থাকলেও একজনের কাজ আরেকজনের কারণে হারাতে পারে: দুজনই Version 1 পড়ল, দুজনই লিখল, আর দ্বিতীয় Write চুপচাপ প্রথমটার ওপর লিখে দিল। এর নাম Lost Update। HTTP-র উত্তর হলো শর্তযুক্ত Request দিয়ে Optimistic Concurrency। প্রতিটা Read-এর সাথে Server একটা Version Tag পাঠায়, যার নাম ETag; লেখার সময় Client সেটা ফেরত পাঠায়:

  • If-Match: "v1": Resource এখনো v1 থাকলে তবেই এটা বসাও। না থাকলে 412 Precondition Failed, কিছুই লেখা হয় না। Client আবার পড়ে আবার চেষ্টা করে।
  • PUT-এ If-None-Match: *: শুধু তৈরি করো, কখনো ওপরে লিখো না (ওখানে আগে থেকে কিছু থাকলে 412)। Amazon S3 ২০২৪ থেকে Write-এ এই দুটো শর্তই সমর্থন করে।
  • If-Unmodified-Since: একই ধারণা, Timestamp দিয়ে, কম নিখুঁত।
  • 428 Precondition Required: শর্ত ছাড়া আসা Write Server ফিরিয়ে দেয়, ফলে Client-কে নিজেকে রক্ষা করতে বাধ্য করে।

Read-এর দিকের জোড়াগুলো (ETag দিয়ে If-None-Match, If-Modified-Since, উত্তর 304) সংঘাত ঠেকায় না, Bandwidth বাঁচায়; ওগুলো Lesson ২.৪-এর বিষয়। ETag আর Versioned API Design করা Chapter 26-এর অংশ।

PUT বনাম PATCH, হারানো Update আর If-Match

দুজন Editor, একটা User Record: {name: Rahim, city: Dhaka, phone: 017…}। ETag হলো Record-এর Version Tag।

পরিস্থিতি

PUT পুরো নতুন অবস্থা পাঠায়। যা বাদ পড়ে, তা হারিয়ে যায়।

Alice-এর App
API Server/users/42name, city=Dhaka, phone
Bob-এর App
PUT /users/42 {"name": "Rahim", "city": "Khulna"}
ধাপ 1 / 3
1

Alice শুধু শহর বদলাতে চায়, তাই তার App পাঠায় শুধু name আর city। কিন্তু PUT-এর মানে “Resource-টা ঠিক এটাই বানাও”।

#POST Retry করা কেন বিপজ্জনক

একটা Request Timeout হলে Client জানতে পারে না তিনটার কোনটা ঘটেছে: Request পৌঁছায়ইনি, Server মাঝপথে Crash করেছে, নাকি Server কাজ শেষ করেছে আর Response হারিয়ে গেছে। GET-এর বেলায় এতে কিছু যায় আসে না। Payment-এর বেলায় এটাই সব।

তোমার লেখা Code ছাড়াও আরও অনেক জায়গায় Retry হয়:

  • Browser আবার ব্যবহার করা Connection কোনো Response আসার আগে বন্ধ হলে Idempotent Request আবার পাঠাতে পারে (RFC 9112 এর অনুমতি দেয়), কিন্তু POST কখনো চুপচাপ না: POST-এর ফলাফলের Page Reload করলে দেখায় “Confirm form resubmission”।
  • Proxy আর Load Balancer একটা Upstream ব্যর্থ হলে আরেকটায় Retry করে। Nginx 1.9.13 Version থেকে, আলাদা করে না বললে, একবার কোনো Upstream-এ পাঠানো Non-idempotent Request (যেমন POST বা PATCH) আর পরের Server-এ পাঠায় না; Envoy আর Service Mesh-এরও প্রতি Route-এ এমন Retry Policy আছে।
  • Client Library Method দেখে Retry করে: যেমন Python-এর urllib3 ডিফল্টভাবে GET, HEAD, PUT, DELETE, OPTIONS আর TRACE Retry করে, POST কখনোই না।
  • HTTP/2 আর HTTP/3 Client-কে জানিয়ে দেয় কোন Stream কখনো প্রক্রিয়া হয়নি (GOAWAY Frame বলে Server শেষ কোন Stream সামলেছে; REFUSED_STREAM একটা Stream ফিরিয়ে দেয়)। ওই Request-গুলো POST হলেও Retry নিরাপদ, কারণ Server নিশ্চয়তা দিচ্ছে সে ওগুলো শুরুই করেনি।
  • TLS 1.3 0-RTT-এর “Early Data” কোনো Attacker আবার চালাতে (Replay) পারে, তাই Server এটা নেয় শুধু Safe Request-এর জন্য, বাকিগুলোর উত্তর 425 Too Early (Lesson ২.৮)।

তাহলে যে Request দুবার চলা চলবে না, যেমন POST /payments, তার কী হবে? নিজেই তাকে Idempotent বানাও, একটা Idempotency Key দিয়ে: প্রথম চেষ্টার আগেই Client একটা অনন্য মান বানায় (সাধারণত এলোমেলো UUID), আর সেই চেষ্টায় ও প্রতিটা Retry-তে সেটা Idempotency-Key Header-এ পাঠায়। Server Key-টা ফলাফলের সাথে জমা রাখে; একই Key আবার এলে কাজটা আবার না করে জমা রাখা Response-টাই ফেরত দেয়। নোটের নিজের উদাহরণ: একজন Customer অর্ডার ৫০০-এর জন্য ৳৫০০০ দেয়, Network Timeout হয়, আর Retry-তে থাকে Idempotency-Key: pay-abc-123, তাই টাকা কাটে একবারই। Header-টা জনপ্রিয় করেছে Stripe, আর এটা এখন IETF-এর একটা খসড়া হিসেবে Standard হচ্ছে।

প্রতিটা ব্যবস্থাকে দুটো কিনারার প্রশ্নের উত্তর দিতে হয়: প্রথমটা এখনো চলছে, এমন সময় একই Key দিয়ে দ্বিতীয় Request (উত্তর 409 Conflict, “চলছে”), আর একই Key-তে আলাদা Body (উত্তর 422, কারণ Client Key-র অপব্যবহার করছে)। Key কোথায় রাখবে, কতদিন রাখবে, আর টাকা কাটা ও জমা রাখা Response কীভাবে একসাথে Commit হবে, সেই Design Chapter 26-এ; Queue আর Webhook-এর Consumer-রাও একই সমস্যায় পড়ে, সেটা Chapter 30-এ।

Response এলোই না: কে Retry করতে পারে, আর তাতে কী হয়

Timeout Client-কে বলে না Server কাজটা করেছে কি না। আবার চেষ্টা করা নিরাপদ কি না, সেটা ঠিক করে Method।

পরিস্থিতি

Request চলার মাঝখানে একটা Upstream Instance মারা যায়। Proxy নিরাপদে আরেকটায় চেষ্টা করতে পারে।

Mobile App
Reverse ProxyNginx / Envoy
Payment API
Ledger DB
GET /payments/981
ধাপ 1 / 4
1

App একটা Payment-এর অবস্থা জানতে চায়।

#Status Code: তিন অঙ্কে Server-এর উত্তর

প্রতিটা Response শুরু হয় তিন অঙ্কের একটা Status Code দিয়ে। প্রথম অঙ্কটা হলো Class, আর নির্দিষ্ট Code-টা আগে কখনো না দেখলেও Client-কে অন্তত এটুকু বুঝতেই হবে: RFC 9110 বলে অচেনা Code-কে তার Class-এর x00 Code-এর মতো ধরতে হবে (অচেনা 4xx মানে 400, অচেনা 5xx মানে 500)। তাই 299 বা 600 বানিয়ে কোনো লাভ নেই।

সংখ্যার পরের লেখাটা (“OK”, “Not Found”) হলো Reason Phrase। এটা শুধু মানুষের জন্য, Client-কে এটা উপেক্ষা করতে হয়, আর HTTP/2 আর HTTP/3 এটা পাঠায়ই না: তারা শুধু সংখ্যাটা পাঠায়, :status Pseudo-header-এ। Reason Phrase মিলিয়ে দেখে এমন Code কখনো লিখো না।

Classমানেকাকে কিছু করতে হবে
1xx Informationalমাঝপথের উত্তর; আসল Response আসছেকাউকে না; Client চালিয়ে যায়
2xx SuccessRequest পৌঁছেছে, বোঝা গেছে, গ্রহণ করা হয়েছেকাউকে না
3xx Redirectionআরও কিছু করতে হবে: অন্য URL-এ যাও, বা নিজের Cache-এর কপি নাওClient, সাধারণত নিজে থেকেই
4xx Client ErrorRequest ভুল বা অনুমতি নেইRetry-র আগে Client-কে কিছু বদলাতে হবে (408, 425, 429 ছাড়া)
5xx Server Errorবৈধ Request-এ Server, বা তার পেছনের কিছু, ব্যর্থ হয়েছেServer Team; Client পরে Retry করতে পারে

#1xx: তথ্য

1xx Response হলো মাঝপথের: আসল Response-এর আগে Server এক বা একাধিক পাঠাতে পারে।

  • 100 Continue. ২ GB-র একটা File Upload করতে যাওয়া Client প্রথমে Expect: 100-continue সহ শুধু Header পাঠিয়ে অপেক্ষা করে। Server হয় 100 দেয় (“এগোও, Body পাঠাও”), নয়তো তখনই 401 বা 413 Content Too Large-এর মতো চূড়ান্ত Code দিয়ে ফিরিয়ে দেয়, পুরো Upload বেঁচে যায়।
  • 101 Switching Protocols. HTTP/1.1-এর Upgrade Request-এর উত্তর, মূলত WebSocket-এর জন্য (Lesson ২.৬)। HTTP/2 আর HTTP/3-এ 101 নেই; তারা Extended CONNECT ব্যবহার করে।
  • 103 Early Hints (RFC 8297)। Server যখন ধীর একটা Page বানাচ্ছে, তখনই Link Header পাঠিয়ে দেয়, যাতে Browser CSS আর Font আগেভাগে আনতে বা অন্য Origin-এ Connect করতে শুরু করে। HTTP/2 Server Push-এর জায়গা এটাই নিয়েছে (Lesson ২.১)।
  • 102 Processing এসেছিল WebDAV থেকে, এখন বাতিল।

#2xx: সফল

  • 200 OK: সাধারণ সফলতা। Body Method-এর ওপর নির্ভর করে: GET-এ Resource, POST-এ একটা ফলাফল।
  • 201 Created: নতুন কিছু তৈরি হয়েছে। সাথে দাও Location: /orders/501 (আর প্রায়ই Body-তে নতুন Resource বা তার ETag)।
  • 202 Accepted: Request প্রক্রিয়ার জন্য নেওয়া হয়েছে, কিন্তু শেষ হয়নি, আর এখনো ব্যর্থ হতে পারে। প্রচলিত Asynchronous নকশায় Location-এ একটা Status URL দেওয়া হয় (চাইলে Retry-After-ও); Client সেটা Poll করে; Job শেষ হলে Status Resource 303 See Other দিয়ে ফলাফলে পাঠায়। কাজে কয়েক সেকেন্ডের বেশি লাগলেই এটা নাও: Report, Video Transcoding, বড় Import, Queue-তে যা যায় সবকিছু (Chapter 30)।
  • 204 No Content: সফল, কিন্তু Body ফাঁকা; PUT, PATCH আর DELETE-এ খুব চলে। Form জমা দিয়ে 204 পেলে Browser একই Page-এ থেকে যায়।
  • 206 Partial Content: Range Request-এর উত্তর। Video Player এটা দিয়ে টেনে সামনে যায়, Download Manager থেমে যাওয়া Download আবার শুরু করে আর ভাগ ভাগ করে একসাথে নামায়। অসম্ভব Range চাইলে উত্তর 416 Range Not Satisfiable।

202 Accepted: লম্বা কাজ, কিন্তু লম্বা Request ছাড়া

মাসিক Report বানাতে প্রায় এক মিনিট লাগে। এতক্ষণ একটা HTTP Request খোলা রাখলে প্রতিটা Proxy-র Timeout-এ সেটা ভাঙে।

পরিস্থিতি

Asynchronous Request-এর প্রচলিত নকশা।

Client
APIapi.example.com
Job Queue
Worker
POST /reports {month: 2026-10}
ধাপ 1 / 10
1

Client একটা Report চায়।

#3xx: Redirect

Redirect Client-কে বলে Location Header-এ দেওয়া URL-এ Request-টা আবার পাঠাও। Code-গুলোর পার্থক্য দুই জায়গায়: স্থানান্তরটা কি স্থায়ী (Client আর Search Engine কি এটা মনে রাখতে পারে?), আর Client-কে কি Method আর Body রেখে দিতে হবে?

Codeনামস্থায়ী?পরের Request-এ Methodডিফল্টে Cacheকখন
301Moved Permanentlyহ্যাঁBrowser POST-কে GET বানায়হ্যাঁপুরোনো Page-এর URL, GET Traffic-এর জন্য http:// → https://
302FoundনাBrowser POST-কে GET বানায়নাপুরোনো ধাঁচের সাময়িক স্থানান্তর (Login Redirect)
303See Otherনাসবসময় GETনাPOST-এর পরে: “ফলাফল ওখানে দেখো” (Post/Redirect/Get), Job-এর ফলাফল
307Temporary Redirectনাএকই থাকে (একই Method আর Body)নাAPI-র সাময়িক স্থানান্তর, Maintenance
308Permanent Redirectহ্যাঁএকই থাকেহ্যাঁযে API POST বা PUT নেয় তার স্থায়ী স্থানান্তর
304Not Modified(স্থানান্তর না)প্রযোজ্য নাপ্রযোজ্য নাশর্তযুক্ত GET-এর উত্তর: “তোমার Cache-এর কপি এখনো ঠিক আছে”

301 আর 302 কেন POST-কে GET বানায়? শুরুর দিকের Browser-গুলো এমন করত, অভ্যাসটা এত ছড়িয়ে গেল যে আর ঠিক করা গেল না, শেষে Specification-ই এর অনুমতি দিল। তাই যেখানে Method টিকিয়ে রাখা জরুরি, তার জন্য 307 আর 308 যোগ করা হলো। কাজের কিছু নিয়ম:

  • স্থায়ী Redirect আঠার মতো লেগে থাকে। Browser 301 বা 308 অনির্দিষ্টকাল Cache করতে পারে; ভুল হলে Server ঠিক করার অনেক পরেও User-রা ভুল জায়গায় যেতে থাকে। আগে 302 বা 307 দিয়ে পরীক্ষা করো।
  • Post/Redirect/Get: Form-এর POST সফল হলে ফলাফল দেখানো Page-এ 303 দিয়ে পাঠাও। সেই Page Reload করলে শুধু একটা নিরীহ GET আবার যায়।
  • Search Engine 301 আর 308-কে ধরে “নতুন URL Index করো আর Ranking-এর সংকেত ওখানে সরাও”; সাময়িক Redirect-এ পুরোনো URL-ই Index-এ থাকে। নোটে যে “SEO Juice”-এর কথা আছে, সেটা এটাই; এটা খাটে স্থায়ী Redirect-এ, 302-এ না।
  • Loop: প্রায় ২০টা Redirect-এর পরে Browser হাল ছেড়ে দেয় (“too many redirects”)। সবচেয়ে চেনা কারণ: TLS শেষ করা Load Balancer-এর পেছনে বসা App সাধারণ HTTP দেখে আর অনন্তকাল HTTPS-এ Redirect করে; সমাধান হলো X-Forwarded-Proto বিশ্বাস করা, বা Redirect শুধু Edge-এ করা।
  • Open Redirect: ?next= Parameter যাচাই না করে Location-এ বসালে Attacker তোমার বিশ্বস্ত Domain থেকে User-দের Phishing Site-এ পাঠাতে পারে। শুধু Relative Path, বা চেনা Host-এর একটা তালিকা মেনে নাও।
  • API Client প্রায়ই Redirect অনুসরণ করে না (curl-এ -L লাগে), আর যারা করে তারাও POST-কে GET বানিয়ে ফেলতে পারে। API-তে Redirect-এর ভরসায় থেকো না; সরাতেই হলে 308 নাও।
  • 304 3xx পরিবারের, কিন্তু স্থানান্তর না: এটা শর্তযুক্ত GET-এর উত্তর, মানে Client-এর জমা রাখা কপি আর Fresh না হলেও যাচাই করে দেখা গেছে এখনো ঠিক আছে, তাই কোনো Body পাঠানো হয় না (নোটে বলা হয়েছে “Cache-এর ফাইলটাই টাটকা”, যেটা একটু ভুল)। Lesson ২.৪-এ বিস্তারিত।

Redirect: 301 বনাম 308 বনাম 307 বনাম 303

প্রতিটা Redirect-এ একটা Location Header থাকে। পার্থক্য হলো সেটা স্থায়ী কি না, আর Browser Method ও Body রেখে দেয় কি না।

পরিস্থিতি

একটা API স্থায়ীভাবে সরে গেছে আর 301 দেয়। একটা Form POST আসে।

Browser
Edge / পুরোনো URLold.example.com
App / নতুন URLwww.example.com
POST /orders {item: 7}
ধাপ 1 / 4
1

একটা পুরোনো Page এখনো পুরোনো Host-এ POST করে।

#4xx: Client-এর ভুল

4xx বলে সমস্যাটা Request-এ, তাই হুবহু একই Request আবার পাঠালে আবার ব্যর্থ হবে (জেনেশুনে রাখা তিনটা ব্যতিক্রম: 408, 425 আর 429)।

Codeনামমানে আর সাধারণ ব্যবহার
400Bad Requestগঠনে ভুল: অবৈধ JSON, দরকারি Header নেই, ভাঙা Query String
401UnauthorizedUnauthenticated: Credential নেই, বা ভুল বা মেয়াদোত্তীর্ণ। কীভাবে পরিচয় দিতে হবে তা জানানো WWW-Authenticate থাকতেই হবে
403Forbiddenপরিচয় জানা (বা লাগেই না), কিন্তু অনুমতি নেই। আবার Login করলেও হবে না
404Not Foundএখানে কোনো Resource নেই, বা এমন একটা আছে যার কথা তোমার জানার অনুমতি নেই
405Method Not AllowedResource আছে, কিন্তু এই Method-এর জন্য না। Allow: GET, HEAD থাকতেই হবে
406Not AcceptableClient-এর Accept-এ চাওয়া কোনো Format Server বানাতে পারে না
408Request TimeoutClient Request পাঠাতে খুব দেরি করেছে; Server Connection বন্ধ করে। Retry নিরাপদ
409ConflictRequest বর্তমান অবস্থার সাথে সাংঘর্ষিক: একই Username আগেই আছে, Version-এর সংঘাত, Idempotency Key এখনো চলছে
410Goneছিল, কিন্তু ইচ্ছে করে, স্থায়ীভাবে সরানো হয়েছে। Search Engine এটা 404-এর চেয়ে দ্রুত বাদ দেয়
412Precondition FailedIf-Match / If-None-Match / If-Unmodified-Since শর্ত মেলেনি
413Content Too LargeBody সীমার চেয়ে বড় (Nginx-এর ডিফল্ট client_max_body_size মাত্র ১ MB)
414URI Too LongURL খুব লম্বা; প্রায়ই এমন একটা GET যেটা POST বা QUERY হওয়া উচিত ছিল
415Unsupported Media TypeBody-র Content-Type চলে না (শুধু JSON নেওয়া API-তে XML, অচেনা Patch Format)
422Unprocessable Contentগঠন ঠিক, কিন্তু মানে অবৈধ: Domain ছাড়া Email, শুরুর আগের শেষ তারিখ
425Too EarlyReplay হতে পারে এমন TLS 1.3 Early Data Server প্রক্রিয়া করবে না; Handshake-এর পরে Retry করো
428Precondition RequiredWrite শর্তযুক্ত হতেই হবে; If-Match পাঠাও
429Too Many RequestsRate Limit লেগেছে। সাথে Retry-After থাকা উচিত
431Request Header Fields Too Largeসাধারণত লাগামছাড়া বেড়ে যাওয়া একটা Cookie
451Unavailable For Legal Reasonsআইনি দাবিতে আটকানো (সংখ্যাটা Fahrenheit 451 বইয়ের দিকে ইশারা)

চারটা জোড়া নিয়েই সবচেয়ে বেশি তর্ক হয়:

  • 401 বনাম 403. নামগুলো ইতিহাসের রেখে যাওয়া। 401 মানে “তুমি কে?”, আর Client-কে পরিচয় দিতে ডাকে; 403 মানে “আমি জানি তুমি কে, তবু না”। যে Client 403 পেয়ে User-কে আবার Login করায়, সে একটা অনন্ত Loop বানায়। Session আর Token আছে Lesson ১.৩-এ; Authorisation-এর মডেল Chapter 34–35-এ।
  • 403 বনাম 404. কিছু একটা আছে, এটা জানানোই যদি তথ্য ফাঁস হয় (অন্য Tenant-এর Invoice, একটা Private Repository), উত্তর দাও 404। RFC 9110 স্পষ্টভাবে এর অনুমতি দেয়, আর GitHub ঠিক এটাই করে।
  • 404 বনাম 410. 404 ভবিষ্যৎ নিয়ে কিছু বলে না; 410 কথা দেয় এটা আর ফিরবে না। ইচ্ছে করে অবসরে পাঠানো Content আর API Version-এর জন্য 410 নাও। দুটোই ডিফল্টে Cacheable, তাই Resource তৈরি হওয়ার পরেও CDN কিছুক্ষণ 404 দিতে থাকতে পারে: Error Response-এ স্পষ্ট Cache-Control বসাও।
  • 400 বনাম 422. অনেক API সবকিছুর জন্যই 400 দেয়; কেউ কেউ পড়াই যায় না এমন Input-এ 400, আর গঠন ঠিক কিন্তু Business Rule ভাঙে এমন Input-এ 422 দেয়। দুটোই চলে। একটা রীতি বেছে নাও, Document করো, আর বিস্তারিত দাও Body-তে।

401 → Login → 200, আর 403 কেন যায় না

401 জিজ্ঞেস করে “তুমি কে?”; Login করলে ঠিক হতে পারে। 403 বলে “আমি জানি তুমি কে, তবু না”; আবার Login করলেও হবে না।

পরিস্থিতি

চেনা পথ।

Web AppBrowser-এ
Invoice APIapi.example.com
Login Serverlogin.example.com
GET /invoices
ধাপ 1 / 6
1

App কোনো Credential ছাড়াই API ডাকে।

429 Too Many Requests নিয়ে আলাদা করে বলা দরকার, কারণ এটা Client-এর Logic-এর ভুল না; এটা Flow Control। Server (সাধারণত একটা API Gateway) বলছে “আস্তে”, আর Retry-After বলে কতক্ষণ, সেকেন্ডে বা একটা HTTP তারিখ হিসেবে। ভদ্র Client অন্তত ততক্ষণ অপেক্ষা করে, এলোমেলো Jitter যোগ করে যাতে হাজারো Client একই মুহূর্তে না ফেরে, আর চেষ্টার একটা সীমা রাখে। অনেক API বরাদ্দটাও জানিয়ে দেয়: IETF RateLimit আর RateLimit-Policy Header Standard করছে, আর পুরোনো API-গুলো পাঠায় X-RateLimit-Limit আর X-RateLimit-Remaining। Rate Limit-এর Algorithm আছে Chapter 29-এ, আর Retry-র কৌশল Chapter 28-এ।

Retry-After সহ 429 আর 503: Client-কে ভদ্রভাবে ধীর করা

সীমা: প্রতি API Key-তে মিনিটে ১০০টা Request। Gateway গোনে; পেছনের API বাড়তি Request দেখেই না।

পরিস্থিতি

যে Client Header পড়ে।

ClientAPI Key k-17
API GatewayRate Limiter100 / 100
API
১০০ নম্বর Request
ধাপ 1 / 6
1

Client এই মিনিটের পুরো বরাদ্দ খরচ করে ফেলেছে।

#5xx: Server-এর ভুল

5xx বলে Request হয়তো ঠিকই ছিল, কিন্তু Server, বা তার পেছনের কিছু, কাজটা শেষ করতে পারেনি। একটা 5xx দেখলে সবচেয়ে কাজের প্রশ্ন হলো কে এটা বানাল: তোমার Application, নাকি তার সামনের কোনো Proxy?

Codeনামসাধারণত বানায়সাধারণ কারণকোথায় দেখবে
500Internal Server ErrorApplicationনা-সামলানো Exception বা অপ্রত্যাশিত অবস্থাওই Request ID ধরে App-এর Log আর Trace
501Not ImplementedServerএই Method সে একদমই সমর্থন করে নাClient, বা Server-এর Method তালিকা
502Bad GatewayProxy / Load BalancerUpstream Connection ফিরিয়ে দিয়েছে বা Reset করেছে, Response-এর মাঝে Crash করেছে, বা বিকৃত Response পাঠিয়েছেUpstream Process: Crash, Restart, Out-of-memory Kill, Keep-alive Timeout
503Service UnavailableProxy (সুস্থ Target নেই) বা App (Load কমাচ্ছে)অতিরিক্ত চাপ, Maintenance, Deploy, ক্ষমতা নেই। সংজ্ঞাতেই সাময়িক; Retry-After পাঠাওHealth Check, ক্ষমতা, Deploy-এর অবস্থা
504Gateway TimeoutProxy / Load BalancerProxy-র Timeout-এর মধ্যে Upstream উত্তর দেয়নি (Nginx-এর proxy_read_timeout আর AWS ALB-এর Idle Timeout, দুটোরই ডিফল্ট ৬০ সেকেন্ড)App কীসের অপেক্ষায় ছিল: ধীর Query, অন্য Service, কোনো Lock
505HTTP Version Not SupportedServerএমন Protocol Version যা সে মানে নাClient

বাস্তব Platform-গুলো নিজেদের Code-ও যোগ করে: Client উত্তর আসার আগেই Connection বন্ধ করলে Nginx 499 Log করে (এটা কখনো তারে পাঠানো হয় না, কিন্তু 499-এর হার বাড়া মানে User-রা হাল ছেড়ে দিচ্ছে); Cloudflare তার আর তোমার Origin-এর মাঝের সমস্যায় 520–527 দেয় (521 Origin Connection ফিরিয়ে দিয়েছে, 522 Connection Timeout, 524 Origin ১০০ সেকেন্ডের বেশি নিয়েছে, 525 TLS Handshake ব্যর্থ)। AWS-এ Target Group-এ কোনো Target Register না থাকলে ALB 503 দেয়; Register করা সব Target অসুস্থ হলে সে “Fail Open” করে, তবুও ওদের কাছেই Traffic পাঠায়, ফলে লক্ষণ হয়ে দাঁড়ায় 502 বা 504।

নোটের এক লাইনের সংজ্ঞাগুলো মূল ভাবে ঠিক; দুটো একটু শাণ দিতে হবে। 502 মানে শুধু “ভুল” Response না: Connection ফিরিয়ে দেওয়া বা Reset হওয়াও এর মধ্যে পড়ে। আর 503 মানে Server বন্ধ করে রাখা না: এটা সাময়িক প্রত্যাখ্যান, প্রায়ই Load Balancer-এর, আর প্রায়ই ইচ্ছাকৃত (যে কাজ সময়মতো শেষ করতে পারবে না তা ফিরিয়ে দিয়ে App নিজেকে বাঁচাচ্ছে)।

এই Status Code কে বানাল? 500 বনাম 502 বনাম 503 বনাম 504

App-এর সামনে একটা Reverse Proxy বা Load Balancer বসে আছে। কে বানিয়েছে জানলে 5xx Code বলে দেয় কোন ধাপে সমস্যা।

পরিস্থিতি

Client
Load BalancerTimeout ৬০ সে.
App Server
Database
GET
ধাপ 1 / 3
1

Client Load Balancer-কে GET /orders/77 পাঠায়, সে একটা সুস্থ App Server বেছে নেয়।

#কোন Response Retry করা নিরাপদ?

ResponseRetry?কীভাবে
Connection Refused, কিছু পাঠানোর আগেইহ্যাঁServer-এ কিছুই পৌঁছায়নি; আবার চেষ্টা করো বা আরেক Instance-এ যাও
পাঠানোর পরে Timeout / Connection Resetশুধু Idempotent হলে, বা Idempotency Key দিয়ে সুরক্ষিত হলেServer হয়তো কাজটা করে ফেলেছে
408, 425হ্যাঁআবার পাঠাও (425-এ Handshake শেষ হওয়ার পরে)
429, 503হ্যাঁ, পরেঅন্তত Retry-After পর্যন্ত অপেক্ষা; না থাকলে Jitter সহ Exponential Backoff
502Idempotent হলেসাধারণত Restart হওয়া বা Crash করা Instance; আরেকটা হয়তো চলবে
504Idempotent হলে, সাবধানেUpstream হয়তো এখনো কাজ করছে, আর Retry যা আগে থেকেই ধীর তার ওপর আরও চাপ দেয়
500বড়জোর একবার, Idempotent হলেসাধারণত Bug: একই Input একই Bug-এ পড়ে
401একবার, Token Refresh করার পরেআবার ব্যর্থ হলে User-কে Login করতে হবে
409, 412বর্তমান অবস্থা আবার পড়ার পরেনতুন Version-এর ওপর পরিবর্তনটা আবার বসাও, বা User-কে জিজ্ঞেস করো
বাকি 4xxনাRequest ঠিক করো

Retry গুণিতক হারেও বাড়ে। একটা Client, একটা Gateway আর একটা Service, প্রত্যেকে তিনবার করে চেষ্টা করলে নিচের একটা ব্যর্থ Call হয়ে দাঁড়ায় ৩ × ৩ × ৩ = ২৭টা চেষ্টা, ঠিক যখন নিচের Service-টা হিমশিম খাচ্ছে। Retry করো শুধু এক স্তরে (সাধারণত User-এর সবচেয়ে কাছেরটায়), একটা Retry Budget রাখো, আর Jitter সহ Backoff করো। Retry, Backoff আর Circuit Breaker ভালো করে আছে Chapter 28-এ।

#যে Error Response কাজে লাগে: Problem Details

Status Code বলে ব্যর্থতাটা কোন ধরনের; Body-র উচিত বলা ঠিক কী ব্যর্থ হলো, এমনভাবে যাতে Program আর মানুষ দুজনেই পড়তে পারে। প্রতিটা API নিজের মতো Error-এর আকার না বানিয়ে Problem Details for HTTP APIs ব্যবহার করো (RFC 9457, ২০২৩, যেটা RFC 7807-এর জায়গা নিয়েছে), সাথে Content-Type: application/problem+json:

HTTP/1.1 422 Unprocessable Content
Content-Type: application/problem+json

{
  "type": "https://example.com/problems/validation",
  "title": "Your request is not valid",
  "status": 422,
  "detail": "2 fields failed validation",
  "instance": "/signup/requests/7f3a",
  "errors": [
    { "field": "email",    "reason": "must contain a domain" },
    { "field": "birthDate", "reason": "must be in the past" }
  ],
  "traceId": "4bf92f3577b34da6"
}
  • type হলো একটা URI যা সমস্যার ধরন চেনায় (সবচেয়ে ভালো হয় যদি সেটা ওই ধরনটা ব্যাখ্যা করা একটা Page হয়); Client সিদ্ধান্ত নেয় এটা দেখে, মানুষের জন্য লেখা Text দেখে না।
  • title একটা ছোট সারাংশ, যা প্রতিবার একই থাকে; detail ব্যাখ্যা করে এই নির্দিষ্ট ঘটনাটা।
  • status HTTP Code-টা আবার লেখে (Body আলাদা করে Log হলে কাজে লাগে); instance এই ঘটনাটাকে চেনায়।
  • বাড়তি Member (errors, traceId, balance) দেওয়া চলে, আর দেওয়াই প্রত্যাশিত।

Error Body-তে কখনো Stack Trace, SQL বা ভেতরের Hostname রেখো না: এগুলো User-এর চেয়ে Attacker-কেই বেশি সাহায্য করে। তার বদলে একটা Trace ID দাও, আর বিস্তারিত রাখো নিজের Log-এ। পুরো API-র Error Format Design (Code, Versioning, Documentation) Chapter 26-এর অংশ।

#যা করা উচিত না

  • ভেতরে Error, বাইরে 200 (200 OK {"success": false, "error": "db down"})। পথের প্রতিটা মেশিন শুধু Status পড়ে: CDN Error-টাকে সফল ভেবে Cache করে, Error-rate Dashboard সবুজ থাকে, Retry Logic চালু হয় না, আর কাজ হলো কি না জানতে প্রতিটা Client-কে প্রতিটা Body খুলে পড়তে হয়। (GraphQL প্রথাগতভাবে errors Array সহ 200 দেয়; নতুন GraphQL-over-HTTP Specification Request-স্তরের ব্যর্থতায় ঠিকঠাক 4xx আর 5xx ব্যবহার করে।)
  • Client-এর ভুলে 500। Validation ব্যর্থতায় 500 দিলে On-call Engineer-কে ডাকা হয়, আর এমন Retry চলে যা কখনো সফল হবে না। Validation-এর উত্তর 400 বা 422।
  • ফাঁকা তালিকার জন্য 404। GET /orders?status=open-এ কিছু না মিললে উত্তর 200 []; 404 মানে Collection-টাই নেই।
  • User Login না করলে 403, বা Login করা কিন্তু অনুমতি নেই এমন User-কে 401: Client বুঝতে পারে না Login Screen দেখাবে, নাকি “অনুমতি নেই” বার্তা।
  • POST নেওয়া API-তে 301: Body ফেলে দেয় আর Method বদলায়। 308 নাও।
  • Side Effect সহ GET আর প্রতিটা Read-এর জন্য POST: প্রথমটা Safety ভাঙে, দ্বিতীয়টা Caching আর স্বয়ংক্রিয় Retry ফেলে দেয়।
  • নিজের বানানো Status Code (299, 600): Client এগুলোকে এমনিতেই 200 আর 500 ধরে। বিস্তারিত দাও Body-তে।
  • Cache Header ছাড়া Error। 404, 405, 410, 414 আর 501 ডিফল্টে Cacheable, তাই CDN পুরোনো একটা 404 দিতেই থাকতে পারে। Error Response-এ ভেবেচিন্তে Cache-Control বসাও (no-store, বা ছোট একটা max-age)।

Error Response: “ভেতরে Error, বাইরে 200” কেন ক্ষতি করে

একটা CDN সফল Response ৫ মিনিট Cache করে; একটা Monitor 5xx Response গোনে। দুজনেই শুধু Status Code পড়ে।

পরিস্থিতি

Database বন্ধ, আর API সেটা একটা 200-এর ভেতরে লুকিয়ে রাখে।

User-রা
CDN
Prices API
Monitoring
GET /prices
ধাপ 1 / 5
1

একজন User দাম জানতে চায়।

#System Design-এ Method আর Status Code

  • Load Balancer-এর Health Check আসলে HTTP Request, বিচার হয় Status Code দেখে: ALB-এর ডিফল্ট সুস্থ Code হলো 200, আর অনেক Team GET /healthz রাখে যা 200 বা 503 দেয়। যে Readiness Endpoint Drain হওয়ার সময় 503 দেয়, সে বন্ধ হওয়ার আগেই Balancer-কে নতুন Traffic পাঠানো থামাতে দেয় (Chapter 25)।
  • Monitoring আর SLO দাঁড়িয়ে থাকে Status Code-এর ওপর। সাধারণ Availability SLI 5xx (আর Timeout)-কে ব্যর্থতা গোনে, 4xx-কে বাদ রাখে, কারণ ভুল Input ফিরিয়ে দেওয়াটাই সঠিক আচরণ। 4xx, 429 আর 499 আলাদা করে দেখো: 4xx হঠাৎ লাফ দিলে প্রায়ই বুঝতে হবে Client-এর একটা Release ভেঙেছে।
  • Circuit Breaker খোলে 5xx আর Timeout-এ, 4xx-এ না; 404-এর ঝড় একটা সুস্থ Service-কে ডাকা বন্ধ করার কারণ না (Chapter 28)।
  • Cache আর CDN Method আর Status Code দেখে ঠিক করে কী জমা রাখবে (Lesson ২.৪; CDN আছে Chapter 23–24-এ)।
  • Async কাজ চলে 202 + Status URL + 303 নকশায়, অথবা Polling-এর বদলে Server-sent Event বা Webhook দিয়ে (Lesson ২.৫, Chapter 26)।
  • gRPC চলে HTTP/2-এর ওপর, কিন্তু প্রায় প্রতিটা Call-এ HTTP 200 দেয় আর নিজের Status (যেমন NOT_FOUND বা UNAVAILABLE) পাঠায় Trailer-এ; Gateway দুটোর মধ্যে অনুবাদ করে (Chapter 26)।
  • Interview-এর দিক থেকে: “PATCH কি Idempotent?” (জরুরি না; Merge বনাম JSON Patch বুঝিয়ে বলো)। “দ্বিতীয় Call 404 দিলে DELETE কি Idempotent?” (হ্যাঁ)। “Payment API-কে Retry-নিরাপদ বানাবে কীভাবে?” (Idempotency Key)। “502 বনাম 504?” (মৃত Upstream বনাম ধীর Upstream, দুটোই Proxy-র বানানো)। “কোন Code Retry করো?” (ওপরের Table)।

#নিজে করে দেখো

curl-এ -i দিলে Response Header আর Status Line দেখায়, -v দিলে সবকিছু, Method সহ। httpbin.org একটা Public Test Service, যা চাও তা-ই উত্তর দেয়।

# HEAD: headers only. Compare Content-Length with what a GET would download.
curl -I https://httpbin.org/image/png

# OPTIONS: which methods does this URL allow? (look at the Allow header)
curl -i -X OPTIONS https://httpbin.org/get

# A wrong method gives 405 with an Allow header
curl -i -X DELETE https://httpbin.org/get

# Ask for any status code to see how your client reacts
curl -i https://httpbin.org/status/429
curl -s -o /dev/null -w '%{http_code}\n' https://httpbin.org/status/503

# Redirects: curl -L follows them. Watch the method on the second request with -v.
# 302 (and 301, 303): curl switches POST to GET; 307 (and 308) keep POST and the body.
curl -v -L -X POST -d 'item=7' 'https://httpbin.org/redirect-to?url=/anything&status_code=302'
curl -v -L -X POST -d 'item=7' 'https://httpbin.org/redirect-to?url=/anything&status_code=307'

# Range request: ask for the first 100 bytes, get 206 Partial Content
curl -i -H 'Range: bytes=0-99' https://httpbin.org/range/1000

# Send an idempotency key (httpbin just echoes it back)
curl -i -X POST https://httpbin.org/anything \
  -H 'Idempotency-Key: 6f1c2a9e-3c1d-4f7a-9a51-2b7e0d4c8f10' \
  -H 'Content-Type: application/json' -d '{"order_id": 500, "amount": 5000}'

Browser-এ Developer Tools খোলো (F12), Network Tab-এ যাও, Preserve log-এ টিক দাও, তারপর যেকোনো Site-এ একটা Form জমা দাও: দেখবে POST, তার পরের Redirect (প্রায়ই 302 বা 303), আর তার পরের GET, প্রত্যেকটার Method সহ। ফলাফলের Page Reload করে দেখো Browser আবার জমা দিতে চায় কি না।

#সারসংক্ষেপ

মূল কথা

  • Method আর Status Code হলো পথের প্রতিটা মেশিনের (Browser, Proxy, CDN, Library, Monitor) সাথে চুক্তি; তারা তোমার App না বুঝেই এগুলো দেখে কাজ করে। এদের মানে একবারই লেখা আছে RFC 9110-এ, আর HTTP/1.1, /2, /3 সবখানে একই।
  • GET পড়ে (Body-র কোনো নির্দিষ্ট মানে নেই), HEAD হলো Body ছাড়া GET, POST Body-টা Resource-এর নিজের নিয়মে প্রক্রিয়া করে (শুধু “তৈরি” না), PUT Client-এর বাছা URL-এ তৈরি বা প্রতিস্থাপন করে, PATCH একটা Patch Document বসায়, DELETE সরায়, OPTIONS জিজ্ঞেস করে কী চলে (CORS Preflight), CONNECT Proxy দিয়ে Tunnel খোলে, TRACE ফেরত পাঠায় আর বন্ধ রাখা হয়। QUERY (Standard হচ্ছে) হলো Body সহ একটা Safe, Idempotent GET।
  • Safe = অবস্থা বদলানোর কোনো অনুরোধ নেই (GET, HEAD, OPTIONS, TRACE): Prefetcher আর Crawler নির্দ্বিধায় পাঠায়। Idempotent = একবার বা বহুবার, প্রভাব একই (Safe Method-গুলো, সাথে PUT আর DELETE): স্বয়ংক্রিয়ভাবে Retry করা যায়। Cacheable = বাস্তবে GET আর HEAD। Idempotent মানে অবস্থা এক, Response না (DELETE → 204, তারপর 404)।
  • PUT সব বদলে দেয়; JSON Merge Patch বদলানো Field পাঠায় (null মুছে দেয়; Array পুরোটা); JSON Patch Atomic Operation পাঠায় (সাধারণভাবে Idempotent না)। Lost Update ঠেকাও If-Match + ETag → 412 দিয়ে।
  • Timeout লুকিয়ে রাখে কাজ হয়েছে কি না। Proxy আর Library কখনো নিজে থেকে POST Retry করে না; জরুরি POST-কে Idempotency-Key দিয়ে Idempotent বানাও (একই Key → জমা রাখা Response; চলছে → 409; আলাদা Body → 422)। পুরো Design Chapter 26-এ।
  • 1xx মাঝপথের (100, 101, 103), 2xx সফল (200, 201 + Location, 202 + Status URL, 204, 206), 3xx Redirect (301/302 POST-কে GET বানাতে পারে, 303 সবসময় GET, 307/308 Method রাখে; 304 = যাচাই করা Cache-এর কপি), 4xx Request ঠিক করো (400, 401 পরিচয় নেই বনাম 403 অনুমতি নেই, 404 বনাম 410, 405 + Allow, 409, 412, 415, 422, 429 + Retry-After), 5xx Server-এর দিকে (500 App-এর Bug; 502 মৃত Upstream, 503 সাময়িকভাবে অপ্রাপ্য, 504 ধীর Upstream, সাধারণত Proxy-র বানানো)।
  • Retry করো 408, 425, 429 আর 503 (অপেক্ষার পরে), 502 আর 504 শুধু Idempotent হলে, 500 বড়জোর একবার, বাকি 4xx কখনোই না; Retry এক স্তরে, Backoff আর Jitter সহ।
  • Error দাও ঠিক Code আর application/problem+json Body দিয়ে (RFC 9457); ভেতরে Error রেখে কখনো 200 না, Validation ব্যর্থতায় কখনো 500 না।