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) |
| HEAD | GET-এর মতোই, কিন্তু শুধু Header পাঠাও | না | 200 |
| POST | এই Body-টা Resource নিজের নিয়মে প্রক্রিয়া করুক | হ্যাঁ | 201 + Location (তৈরি হলে), 200 (ফলাফল), 202 (Queue-তে), 204 |
| PUT | এই URL-এর Resource-টা ঠিক এটাই বানাও (তৈরি বা প্রতিস্থাপন) | হ্যাঁ, পুরো রূপটা | 201 (তৈরি), 200 বা 204 (বদলানো) |
| PATCH | Resource-এ এই পরিবর্তনগুলো বসাও | হ্যাঁ, একটা Patch Document | 200 বা 204 |
| DELETE | এই URL-এর Resource সরিয়ে দাও | কোনো নির্দিষ্ট মানে নেই | 204, 200, বা 202 (মোছা পরে হবে) |
| OPTIONS | এই Resource নিয়ে আমি কী কী করতে পারি? | কদাচিৎ | 204 বা 200, সাথে Allow Header |
| CONNECT | Proxy, এই 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-এ যা ছিল তা বাতিল করে দেয়।
| Method | Safe | Idempotent | Cacheable | কেন |
|---|---|---|---|---|
| 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-এর মতোই আচরণ করে।
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)।
| PUT | JSON Merge Patch | JSON Patch | |
|---|---|---|---|
| Body | পুরো Resource | শুধু বদলানো Field | Operation-এর তালিকা |
| 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 পাঠায় শুধু 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 কখনো প্রক্রিয়া হয়নি (
GOAWAYFrame বলে 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 নিরাপদে আরেকটায় চেষ্টা করতে পারে।
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 Success | Request পৌঁছেছে, বোঝা গেছে, গ্রহণ করা হয়েছে | কাউকে না |
| 3xx Redirection | আরও কিছু করতে হবে: অন্য URL-এ যাও, বা নিজের Cache-এর কপি নাও | Client, সাধারণত নিজে থেকেই |
| 4xx Client Error | Request ভুল বা অনুমতি নেই | 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-এর
UpgradeRequest-এর উত্তর, মূলত WebSocket-এর জন্য (Lesson ২.৬)। HTTP/2 আর HTTP/3-এ 101 নেই; তারা Extended CONNECT ব্যবহার করে। - 103 Early Hints (RFC 8297)। Server যখন ধীর একটা Page বানাচ্ছে, তখনই
LinkHeader পাঠিয়ে দেয়, যাতে 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:
RangeRequest-এর উত্তর। Video Player এটা দিয়ে টেনে সামনে যায়, Download Manager থেমে যাওয়া Download আবার শুরু করে আর ভাগ ভাগ করে একসাথে নামায়। অসম্ভব Range চাইলে উত্তর 416 Range Not Satisfiable।
202 Accepted: লম্বা কাজ, কিন্তু লম্বা Request ছাড়া
মাসিক Report বানাতে প্রায় এক মিনিট লাগে। এতক্ষণ একটা HTTP Request খোলা রাখলে প্রতিটা Proxy-র Timeout-এ সেটা ভাঙে।
পরিস্থিতি
Asynchronous Request-এর প্রচলিত নকশা।
Client একটা Report চায়।
#3xx: Redirect
Redirect Client-কে বলে Location Header-এ দেওয়া URL-এ Request-টা আবার পাঠাও। Code-গুলোর পার্থক্য দুই জায়গায়: স্থানান্তরটা কি স্থায়ী (Client আর Search Engine কি এটা মনে রাখতে পারে?), আর Client-কে কি Method আর Body রেখে দিতে হবে?
| Code | নাম | স্থায়ী? | পরের Request-এ Method | ডিফল্টে Cache | কখন |
|---|---|---|---|---|---|
| 301 | Moved Permanently | হ্যাঁ | Browser POST-কে GET বানায় | হ্যাঁ | পুরোনো Page-এর URL, GET Traffic-এর জন্য http:// → https:// |
| 302 | Found | না | Browser POST-কে GET বানায় | না | পুরোনো ধাঁচের সাময়িক স্থানান্তর (Login Redirect) |
| 303 | See Other | না | সবসময় GET | না | POST-এর পরে: “ফলাফল ওখানে দেখো” (Post/Redirect/Get), Job-এর ফলাফল |
| 307 | Temporary Redirect | না | একই থাকে (একই Method আর Body) | না | API-র সাময়িক স্থানান্তর, Maintenance |
| 308 | Permanent Redirect | হ্যাঁ | একই থাকে | হ্যাঁ | যে API POST বা PUT নেয় তার স্থায়ী স্থানান্তর |
| 304 | Not 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 আসে।
একটা পুরোনো Page এখনো পুরোনো Host-এ POST করে।
#4xx: Client-এর ভুল
4xx বলে সমস্যাটা Request-এ, তাই হুবহু একই Request আবার পাঠালে আবার ব্যর্থ হবে (জেনেশুনে রাখা তিনটা ব্যতিক্রম: 408, 425 আর 429)।
| Code | নাম | মানে আর সাধারণ ব্যবহার |
|---|---|---|
| 400 | Bad Request | গঠনে ভুল: অবৈধ JSON, দরকারি Header নেই, ভাঙা Query String |
| 401 | Unauthorized | Unauthenticated: Credential নেই, বা ভুল বা মেয়াদোত্তীর্ণ। কীভাবে পরিচয় দিতে হবে তা জানানো WWW-Authenticate থাকতেই হবে |
| 403 | Forbidden | পরিচয় জানা (বা লাগেই না), কিন্তু অনুমতি নেই। আবার Login করলেও হবে না |
| 404 | Not Found | এখানে কোনো Resource নেই, বা এমন একটা আছে যার কথা তোমার জানার অনুমতি নেই |
| 405 | Method Not Allowed | Resource আছে, কিন্তু এই Method-এর জন্য না। Allow: GET, HEAD থাকতেই হবে |
| 406 | Not Acceptable | Client-এর Accept-এ চাওয়া কোনো Format Server বানাতে পারে না |
| 408 | Request Timeout | Client Request পাঠাতে খুব দেরি করেছে; Server Connection বন্ধ করে। Retry নিরাপদ |
| 409 | Conflict | Request বর্তমান অবস্থার সাথে সাংঘর্ষিক: একই Username আগেই আছে, Version-এর সংঘাত, Idempotency Key এখনো চলছে |
| 410 | Gone | ছিল, কিন্তু ইচ্ছে করে, স্থায়ীভাবে সরানো হয়েছে। Search Engine এটা 404-এর চেয়ে দ্রুত বাদ দেয় |
| 412 | Precondition Failed | If-Match / If-None-Match / If-Unmodified-Since শর্ত মেলেনি |
| 413 | Content Too Large | Body সীমার চেয়ে বড় (Nginx-এর ডিফল্ট client_max_body_size মাত্র ১ MB) |
| 414 | URI Too Long | URL খুব লম্বা; প্রায়ই এমন একটা GET যেটা POST বা QUERY হওয়া উচিত ছিল |
| 415 | Unsupported Media Type | Body-র Content-Type চলে না (শুধু JSON নেওয়া API-তে XML, অচেনা Patch Format) |
| 422 | Unprocessable Content | গঠন ঠিক, কিন্তু মানে অবৈধ: Domain ছাড়া Email, শুরুর আগের শেষ তারিখ |
| 425 | Too Early | Replay হতে পারে এমন TLS 1.3 Early Data Server প্রক্রিয়া করবে না; Handshake-এর পরে Retry করো |
| 428 | Precondition Required | Write শর্তযুক্ত হতেই হবে; If-Match পাঠাও |
| 429 | Too Many Requests | Rate Limit লেগেছে। সাথে Retry-After থাকা উচিত |
| 431 | Request Header Fields Too Large | সাধারণত লাগামছাড়া বেড়ে যাওয়া একটা Cookie |
| 451 | Unavailable 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 করলেও হবে না।
পরিস্থিতি
চেনা পথ।
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 পড়ে।
Client এই মিনিটের পুরো বরাদ্দ খরচ করে ফেলেছে।
#5xx: Server-এর ভুল
5xx বলে Request হয়তো ঠিকই ছিল, কিন্তু Server, বা তার পেছনের কিছু, কাজটা শেষ করতে পারেনি। একটা 5xx দেখলে সবচেয়ে কাজের প্রশ্ন হলো কে এটা বানাল: তোমার Application, নাকি তার সামনের কোনো Proxy?
| Code | নাম | সাধারণত বানায় | সাধারণ কারণ | কোথায় দেখবে |
|---|---|---|---|---|
| 500 | Internal Server Error | Application | না-সামলানো Exception বা অপ্রত্যাশিত অবস্থা | ওই Request ID ধরে App-এর Log আর Trace |
| 501 | Not Implemented | Server | এই Method সে একদমই সমর্থন করে না | Client, বা Server-এর Method তালিকা |
| 502 | Bad Gateway | Proxy / Load Balancer | Upstream Connection ফিরিয়ে দিয়েছে বা Reset করেছে, Response-এর মাঝে Crash করেছে, বা বিকৃত Response পাঠিয়েছে | Upstream Process: Crash, Restart, Out-of-memory Kill, Keep-alive Timeout |
| 503 | Service Unavailable | Proxy (সুস্থ Target নেই) বা App (Load কমাচ্ছে) | অতিরিক্ত চাপ, Maintenance, Deploy, ক্ষমতা নেই। সংজ্ঞাতেই সাময়িক; Retry-After পাঠাও | Health Check, ক্ষমতা, Deploy-এর অবস্থা |
| 504 | Gateway Timeout | Proxy / Load Balancer | Proxy-র Timeout-এর মধ্যে Upstream উত্তর দেয়নি (Nginx-এর proxy_read_timeout আর AWS ALB-এর Idle Timeout, দুটোরই ডিফল্ট ৬০ সেকেন্ড) | App কীসের অপেক্ষায় ছিল: ধীর Query, অন্য Service, কোনো Lock |
| 505 | HTTP Version Not Supported | Server | এমন 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 Balancer-কে GET /orders/77 পাঠায়, সে একটা সুস্থ App Server বেছে নেয়।
#কোন Response Retry করা নিরাপদ?
| Response | Retry? | কীভাবে |
|---|---|---|
| Connection Refused, কিছু পাঠানোর আগেই | হ্যাঁ | Server-এ কিছুই পৌঁছায়নি; আবার চেষ্টা করো বা আরেক Instance-এ যাও |
| পাঠানোর পরে Timeout / Connection Reset | শুধু Idempotent হলে, বা Idempotency Key দিয়ে সুরক্ষিত হলে | Server হয়তো কাজটা করে ফেলেছে |
| 408, 425 | হ্যাঁ | আবার পাঠাও (425-এ Handshake শেষ হওয়ার পরে) |
| 429, 503 | হ্যাঁ, পরে | অন্তত Retry-After পর্যন্ত অপেক্ষা; না থাকলে Jitter সহ Exponential Backoff |
| 502 | Idempotent হলে | সাধারণত Restart হওয়া বা Crash করা Instance; আরেকটা হয়তো চলবে |
| 504 | Idempotent হলে, সাবধানে | 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ব্যাখ্যা করে এই নির্দিষ্ট ঘটনাটা।statusHTTP 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 প্রথাগতভাবেerrorsArray সহ 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 দাম জানতে চায়।
#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+jsonBody দিয়ে (RFC 9457); ভেতরে Error রেখে কখনো 200 না, Validation ব্যর্থতায় কখনো 500 না।
আগে নিজে উত্তর দাও, তারপর ব্যাখ্যাটা পড়ো। ঠিক হলেও পড়ো, কারণ ভুল Option-গুলো কেন ভুল, সেটাও ওখানে আছে।
নিজেকে যাচাই করোপ্রশ্ন
কোন কথাটা Idempotent Method-এর সঠিক সংজ্ঞা?
নিজেকে যাচাই করোপ্রশ্ন
কোন দুটো Method Safe?
বেছে নাও ২
নিজেকে যাচাই করোপ্রশ্ন
একটা Client DELETE /users/42 পাঠিয়ে 204 পেল। একই Request-এর Retry পেল 404। DELETE কি তবুও Idempotent?
নিজেকে যাচাই করোপ্রশ্ন
POST /users আর PUT /users/42, দুটো দিয়েই User তৈরি করা যায়। আসল পার্থক্য কী?
নিজেকে যাচাই করোপ্রশ্ন
একটা Client Content-Type application/merge-patch+json দিয়ে PATCH /users/42 পাঠাল, Body {city: Khulna, phone: null}। কী হবে?
নিজেকে যাচাই করোপ্রশ্ন
একটা Shopping App PATCH /carts/9 পাঠায় এমন JSON Patch দিয়ে, যা /items/- এ একটা Item যোগ করে। Reset-এর পরে একটা Proxy এটা Retry করল, আর এখন Cart-এ Item দুবার। কোন দুটো পরিবর্তন এটা ঠেকায়?
বেছে নাও ২
নিজেকে যাচাই করোপ্রশ্ন
একটা Search API-র ৪০টা Filter শর্ত দরকার, URL-এর জন্য বড্ড লম্বা। একজন Developer এগুলো GET-এর Body-তে পাঠাতে চায়। আজকের দিনে সবচেয়ে ভালো পরামর্শ কী?
নিজেকে যাচাই করোপ্রশ্ন
দুজন Support Agent একই Customer Record খোলে। Agent A নতুন ঠিকানা Save করে; দশ সেকেন্ড পরে Agent B নতুন Phone Number Save করে এমন Form থেকে যেটায় এখনো পুরোনো ঠিকানা। নতুন ঠিকানা হারিয়ে গেল। HTTP-র প্রচলিত সমাধান কী?
নিজেকে যাচাই করোপ্রশ্ন
একটা Upload Service-এ Client নিজেই PUT /files/name দিয়ে File-এর নাম বাছে। দুজন User report.pdf নামে আলাদা দুটো File Upload করে। দ্বিতীয়টা যেন চুপচাপ প্রথমটার জায়গা না নেয়, API সেটা কীভাবে ঠেকাবে?
নিজেকে যাচাই করোপ্রশ্ন
একটা Mobile App ৳৫০০০-এর জন্য POST /payments পাঠাল। Request Timeout হলো। App জানে না Customer-এর টাকা কাটা হয়েছে কি না। App যেন নিরাপদে Retry করতে পারে, Payment API-র কী দেওয়া উচিত?
নিজেকে যাচাই করোপ্রশ্ন
একই Key দিয়ে ৳৫০০০-এর প্রথম Payment সফল হওয়ার পরে একটা Client idempotency key pay-abc-123 আবার ব্যবহার করল ৳৭০০০-এর দ্বিতীয় Payment-এর জন্য। Server-এর কী করা উচিত?
নিজেকে যাচাই করোপ্রশ্ন
Nginx POST /payments পাঠাল Upstream A-তে, যে উত্তর দেওয়ার আগেই Connection Reset করে দিল। Upstream B সুস্থ। ডিফল্ট Setting-এ Nginx কী করবে?
নিজেকে যাচাই করোপ্রশ্ন
Order Form জমা দেওয়ার পরে যারা Confirmation Page-এ Reload চাপে, তাদের কারও কারও দ্বিতীয় Order তৈরি হয়ে যায়। প্রচলিত সমাধান কী?
নিজেকে যাচাই করোপ্রশ্ন
একটা API স্থায়ীভাবে api.example.com থেকে api2.example.com-এ সরে যাচ্ছে। Client-রা JSON Body সহ POST আর PUT পাঠায়। পুরোনো Host কোন Redirect দেবে?
নিজেকে যাচাই করোপ্রশ্ন
কোন দুটো Redirect Code Client-কে আসল Method আর Body রেখে দিতে বাধ্য করে?
বেছে নাও ২
নিজেকে যাচাই করোপ্রশ্ন
একটা Browser If-None-Match: abc সহ GET /logo.png পাঠিয়ে 304 Not Modified পেল। এর মানে কী?
নিজেকে যাচাই করোপ্রশ্ন
Site-টা TLS শেষ করা একটা Load Balancer-এর পেছনে বসানোর পরে প্রতিটা Page-এ too many redirects দেখাচ্ছে। App HTTP-কে HTTPS-এ Redirect করে। গোলমাল কোথায়?
নিজেকে যাচাই করোপ্রশ্ন
Viewer Role-এর একজন Login করা User DELETE /invoices/7 ডাকল। API কী দেবে, আর Client কী করবে?
নিজেকে যাচাই করোপ্রশ্ন
401 Unauthorized Response-এ কোন Header থাকতেই হবে?
নিজেকে যাচাই করোপ্রশ্ন
একটা Multi-tenant SaaS-এ Company A-র একজন User /invoices/9001 চাইল, যেটা Company B-র। কোন Response সবচেয়ে কম তথ্য ফাঁস করে?
নিজেকে যাচাই করোপ্রশ্ন
একটা বার্ষিক Report বানাতে ২–৩ মিনিট লাগে। সবচেয়ে ভালো HTTP Design কোনটা?
নিজেকে যাচাই করোপ্রশ্ন
একটা Client GET /jobs/42 Poll করছে, আর Job-টা ব্যর্থ হয়েছে। Poll-এর উত্তরে কোন Status দেওয়া উচিত?
নিজেকে যাচাই করোপ্রশ্ন
একটা API Retry-After: 30 সহ 429 Too Many Requests দিল। হাজার হাজার Client একই মুহূর্তে এটা পেল। প্রতিটা Client-এর কী করা উচিত?
নিজেকে যাচাই করোপ্রশ্ন
GET-এর কোন দুটো Response স্বয়ংক্রিয়ভাবে (Backoff সহ) Retry করা যুক্তিসংগত?
বেছে নাও ২
নিজেকে যাচাই করোপ্রশ্ন
Load Balancer-এর Log-এ POST /reports-এর জন্য ৬০ সেকেন্ড পরে 504, অথচ App-এর Log বলছে একই Request ৮৫ সেকেন্ডে সফলভাবে শেষ। যারা Retry করেছে তারা দুটো Report পেয়েছে। মূল কারণ আর সমাধান কী?
নিজেকে যাচাই করোপ্রশ্ন
কোনটা 503 Service Unavailable-এর সাধারণ কারণ না?
নিজেকে যাচাই করোপ্রশ্ন
একটা বিভ্রাটের সময় API {error: database unavailable} Body সহ 200 OK দেয়। এতে কোন দুটো সমস্যা হয়?
বেছে নাও ২
নিজেকে যাচাই করোপ্রশ্ন
HTTP API-র Error-এর জন্য RFC 9457 কোন Body Format ঠিক করে দেয়?
নিজেকে যাচাই করোপ্রশ্ন
একটা পুরোনো Client Library 450 Status Code পেল, যেটা সে চেনে না। HTTP-র নিয়ম অনুযায়ী Response-টাকে কীভাবে ধরতে হবে?
নিজেকে যাচাই করোপ্রশ্ন
একটা Mobile App একটা API Gateway-কে ডাকে, Gateway ডাকে Orders Service-কে, সে ডাকে Payments Service-কে। প্রতিটা স্তর ব্যর্থ Call সর্বোচ্চ ৩ বার Retry করে। Payments Service বন্ধ। User-এর একবার বোতাম চাপায় Payments-এ সর্বোচ্চ কতটা Request যেতে পারে?
নিজেকে যাচাই করোপ্রশ্ন
কোনো স্পষ্ট Caching Header ছাড়াই (ডিফল্টে Heuristically Cacheable) Shared Cache কোন দুটো Response জমা রেখে আবার দিতে পারে?
বেছে নাও ২