HTTP কী? সার্ভার অ্যাডমিনদের জন্য পূর্ণাঙ্গ নির্দেশিকা
HTTP কীভাবে কাজ করে তা জানুন। সার্ভার অ্যাডমিনদের জন্য মেথড, স্ট্যাটাস কোড, হেডার, Nginx লগ এবং TLS ও HTTP/3 প্রোটোকলের খুঁটিনাটি এই গাইডে সহজভাবে ব্যাখ্যা করা হয়েছে।
HTTP কী?
HTTP (hypertext transfer protocol) হলো এমন কিছু নিয়মের সমষ্টি যা ব্যবহার করে একটি ক্লায়েন্ট এবং একটি ওয়েব সার্ভার কোনো কিছুর অনুরোধ জানায় এবং তার উত্তর পাঠায়। ক্লায়েন্ট একটি অনুরোধ পাঠায়: যেমন GET-এর মতো একটি মেথড, /pricing-এর মতো একটি পাথ, একটি প্রোটোকল ভার্সন, হেডারগুলোর একটি তালিকা এবং কখনো কখনো একটি বডি। সার্ভার 200-এর মতো একটি স্ট্যাটাস কোড দিয়ে উত্তর দেয়, যার সাথে থাকে সার্ভারের নিজস্ব হেডার এবং সাধারণত একটি বডি। আপনার সার্ভারে প্রতিটি পেজ ভিউ এবং প্রতিটি API (application programming interface) কল হলো এই আদান-প্রদানের একটি পুনরাবৃত্তি।
HTTP-এর নিজস্ব কোনো স্টেট (state) থাকে না। সার্ভার মনে রাখতে পারে না যে আপনি এক সেকেন্ড আগে কী অনুরোধ করেছিলেন, তাই মেমোরির মতো কাজ করে এমন যেকোনো কিছু, যেমন একটি লগইন সেশন, প্রতিটি অনুরোধের হেডারে বহন করা হয়। এই একটি বৈশিষ্ট্যই পরবর্তী অনেক কিছুর ব্যাখ্যা দেয়: ক্যাশিং (caching) সম্পূর্ণভাবে হেডার দ্বারা নিয়ন্ত্রিত হয় এবং একটি লোড ব্যালেন্সার কোনো সমস্যা ছাড়াই আপনার পরবর্তী অনুরোধটি অন্য একটি ব্যাকএন্ডে পাঠাতে পারে।
নিচের সবকিছুই হলো সার্ভারের দিক থেকে সেই মডেলের রূপ, যা আপনার access log এবং nginx config-এ দেখা যায়।
একটি raw অনুরোধ এবং প্রতিক্রিয়া, ব্যাখ্যাসহ
নিচে একটি সম্পূর্ণ HTTP/1.1 অনুরোধ দেওয়া হলো। একটি ফাঁকা লাইন হেডারগুলোর সমাপ্তি নির্দেশ করে এবং সেই লাইনের পরের সবকিছুই হলো বডি। একটি GET-এর সাধারণত কোনো বডি থাকে না।
GET /pricing HTTP/1.1
Host: example.com
User-Agent: curl/8.5.0
Accept: */*
Accept-Encoding: gzipGETহলো মেথড, যা নির্দেশ করে আপনি কী করতে চান।GETডেটা পড়ে,POSTডেটা পাঠায়,PUTপ্রতিস্থাপন করে,DELETEমুছে ফেলে,HEADবডি ছাড়া শুধুমাত্র একটিGET-এর হেডারগুলো জানতে চায়।/pricingহলো পাথ। হোস্টনেম অনুরোধের লাইনের অংশ নয়, আর তাই পরবর্তী হেডারটি প্রয়োজন হয়।HTTP/1.1হলো প্রোটোকল ভার্সন যা ক্লায়েন্ট ব্যবহার করছে।Host: example.comসেই সাইটের নাম নির্দেশ করে যা ক্লায়েন্ট চায়। HTTP/1.1-এ এটি বাধ্যতামূলক, তাই কোনো হেডার ছাড়া অনুরোধ আসলে nginx400 Bad Requestদিয়ে উত্তর দেয়।- বাকিগুলো হলো পছন্দ বা প্রেফারেন্স।
Accept-Encoding: gzipজানায় যে ক্লায়েন্ট ডিকম্প্রেস করতে পারে, তাই সার্ভার বডিটি কম্প্রেস করে পাঠাতে পারবে।
প্রতিক্রিয়ার গঠনও একই, তবে উপরে একটি স্ট্যাটাস লাইন থাকে।
HTTP/1.1 200 OK
Date: Thu, 06 Aug 2026 09:12:44 GMT
Server: nginx
Content-Type: text/html; charset=utf-8
Content-Length: 5310
Cache-Control: public, max-age=300
<!doctype html>...200 OKহলো স্ট্যাটাস কোড এবং এর সাথে থাকা রিজন ফ্রেজ। কোডটিই আসল, ফ্রেজটি কেবল সাজসজ্জা এবং ক্লায়েন্টরা এটি উপেক্ষা করে।Content-Typeক্লায়েন্টকে জানায় যে পরবর্তী বাইটগুলো কীভাবে প্রসেস করতে হবে।Content-Lengthহলো বাইটে বডির আকার, যাতে ক্লায়েন্ট বুঝতে পারে বডি কোথায় শেষ হয়েছে। আকার আগে থেকে জানা না থাকলে সার্ভারTransfer-Encoding: chunkedপাঠায় এবং শূন্য দৈর্ঘ্যের একটি চাঙ্ক দিয়ে সমাপ্তি নির্দেশ করে।Cache-Controlব্রাউজার এবং মাঝখানের যেকোনো ক্যাশকে জানায় যে এই প্রতিক্রিয়াটি কতক্ষণ পর্যন্ত ফ্রেশ হিসেবে গণ্য করা যাবে।max-ageসেকেন্ড পার হয়ে গেলে এটি stale বা বাসি হয়ে যায়, তবে এর মানে এই নয় যে এটি মুছে ফেলা হয়েছে: একটি ক্যাশ বাসি কপিটি রেখে দিতে পারে এবং পুরো বডি পুনরায় ডাউনলোড না করে আপনার সার্ভারের সাথে তা যাচাই (revalidate) করতে পারে।- হেডারগুলোর পরের ফাঁকা লাইনটি উভয় ক্ষেত্রেই হেডার এবং বডিকে আলাদা করে।
হেডারের নামগুলো case insensitive, এবং প্রতিটি লাইন একটি carriage return এবং তার পরে একটি line feed দিয়ে শেষ হয়, শুধু একটি newline দিয়ে নয়। আপনি এগুলো হাতে টাইপ করবেন না, তবে প্যাকেট ক্যাপচারের সময় এগুলোর মুখোমুখি হবেন।
একটি বাস্তব জোড়া পর্যবেক্ষণ করতে, আপনার নিজের কোনো সাইটে এটি চালান:
curl -sS -o /dev/null -D - https://example.com/-D - রেসপন্স হেডারগুলো আপনার টার্মিনালে লেখে এবং -o /dev/null বডিটি বাতিল করে দেয়। curl -I-এর পরিবর্তে এটি ব্যবহার করা ভালো, কারণ -I একটি HEAD অনুরোধ পাঠায়। একটি অ্যাপ্লিকেশন সার্ভার যা HEAD-কে GET থেকে ভিন্নভাবে হ্যান্ডেল করে (এবং অনেকেই তা করে), সেটি আপনাকে এমন হেডার দেখাবে যা কোনো ব্রাউজার কখনোই পায় না। curl -v উভয় দিকই প্রিন্ট করে, যেখানে অনুরোধের লাইনগুলো > এবং প্রতিক্রিয়ার লাইনগুলো < দিয়ে চিহ্নিত থাকে।
nginx access log-এ request line কেমন দেখায়
nginx একটি combined log format নিয়ে আসে। এর সংজ্ঞা হলো:
log_format combined '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent"';এটি যে একটি line তৈরি করে:
203.0.113.45 - - [06/Aug/2026:09:12:44 +0000] "GET /pricing HTTP/1.1" 200 5310 "https://example.com/" "Mozilla/5.0 (X11; Linux x86_64) Chrome/127.0.0.0 Safari/537.36"203.0.113.45হলো$remote_addr, অর্থাৎ যে address TCP (transmission control protocol) connection খুলেছে। Proxy-এর পেছনে এটি visitor-এর address নয়, proxy-এর address হবে—যদি না আপনি নিচে বর্ণিত realip module configure করে এটিকে প্রতিস্থাপন করেন।- প্রথম
-একটি নির্দিষ্ট placeholder। দ্বিতীয়টি হলো$remote_user, যা শুধু HTTP basic authentication ব্যবহার হলে পূরণ হয়। "GET /pricing HTTP/1.1"হলো$request, অর্থাৎ সম্পূর্ণ original request line। nginx-এর default log escaping অনুযায়ী double quote, backslash এবং 32-এর নিচে বা 126-এর ওপরে থাকা যেকোনো byte\xXXহিসেবে লেখা হয়। তাই line-টি মূল বিষয়বস্তুর সঙ্গে সামঞ্জস্যপূর্ণ থাকে, তবে সব সময় byte-for-byte এক হয় না।200হলো আপনার server ফেরত দেওয়া status, visitor যে status দেখেছে তা নয়।5310হলো$body_bytes_sent, অর্থাৎ শুধু body। Response header গণনা করা হয় না। তাই এই সংখ্যা বাস্তবে পাঠানো মোট byte-এর চেয়ে সব সময় ছোট।- উদ্ধৃত শেষ দুটি field হলো
RefererএবংUser-Agent। দুটিই client থেকে আসে। তাই দুটিতে যেকোনো কিছু থাকতে পারে।
অপ্রত্যাশিত data-ও escaped form-এ log হয়। কোনো client আপনার plaintext port 80-এ TLS (transport layer security) ব্যবহার করে কথা বললে একটি 400 line তৈরি হয়। এর request field "\x16\x03\x01\x02\x00\x01"-এর মতো escaped byte দিয়ে শুরু হয়। \x16 হলো TLS handshake record-এর type। তাই ওই byte-গুলো ClientHello-এর শুরু, request line নয়। আপনার server সঠিকভাবেই কাজ করছে। কোনো কিছু HTTPS-কে HTTP port-এ পাঠাচ্ছে।
আপনার log format-এ $server_protocol-ও যোগ করুন। এটি HTTP/1.1, HTTP/2.0 অথবা HTTP/3.0 দেখায়। কোনো protocol change সত্যিই কার্যকর হয়েছে কি না প্রমাণ করার এটি সবচেয়ে দ্রুত উপায়।
আপনার সাইট থেকে আসা সাধারণ স্ট্যাটাস কোডগুলোর অর্থ
প্রথম অঙ্কটি হলো ক্লাসের নির্দেশক, তাই প্রথমেই সেটি দেখা উচিত।
2xx মানে অনুরোধ সফল হয়েছে। সাধারণ রিডের জন্য 200 OK। কোনো কিছু তৈরি করার পর POST-এর প্রতিক্রিয়ায় 201 Created। কোনো কিছু ফেরত পাঠানোর প্রয়োজন না হলে 204 No Content, যা সাধারণত DELETE-এর উত্তর হিসেবে আসে।
3xx মানে অন্য কোথাও দেখুন। 301 স্থায়ী এবং ব্রাউজার এটি শক্তভাবে ক্যাশ করে রাখে, অনেক সময় ব্যবহারকারী প্রোফাইল ক্লিয়ার না করা পর্যন্ত এটি থেকে যায়, তাই ভুল হোস্টনামে নির্দেশিত 301 সংশোধন করা বেশ কষ্টসাধ্য। রিডাইরেক্ট পরীক্ষা করার সময় 302 ব্যবহার করুন। 304 Not Modified কোনো ত্রুটি নয়, বরং একটি সাফল্য: ক্লায়েন্ট একটি শর্তসাপেক্ষ অনুরোধ পাঠিয়েছে, হয় If-None-Match যাতে একটি ETag (entity tag) আছে যা আপনি এখনো শনাক্ত করতে পারছেন, অথবা If-Modified-Since যাতে এমন একটি তারিখ আছে যার পর থেকে রিসোর্সটি পরিবর্তিত হয়নি, তাই আপনি শুধু হেডার পাঠিয়েছেন কিন্তু বডি পাঠাননি। লগে প্রচুর 304 থাকা মানে ক্যাশিং সঠিকভাবে কাজ করছে।
4xx মানে অনুরোধটি ভুল ছিল। 400 Bad Request মানে ইনপুট ত্রুটিপূর্ণ। 401 Unauthorized মানে প্রমাণীকরণের অভাব, এবং এতে অবশ্যই একটি WWW-Authenticate হেডার থাকতে হবে যা স্কিমের নাম উল্লেখ করবে। 403 Forbidden মানে অনুরোধটি বোঝা গেছে কিন্তু তা প্রত্যাখ্যান করা হয়েছে। 404 Not Found মানে এমন একটি পাথ যা বিদ্যমান নেই, অথবা সার্ভার এটি স্বীকার করতে চায় না, কারণ কিছু অ্যাপ্লিকেশন 403-এর পরিবর্তে 404 উত্তর দেয় যাতে অ্যাক্সেস নেই এমন ক্লায়েন্টদের কাছ থেকে রিসোর্সটি লুকিয়ে রাখা যায়। 405 Method Not Allowed মানে পাথ সঠিক কিন্তু মেথড ভুল, যা কোনো স্ট্যাটিক ফাইল লোকেশনে POST পাঠালে পাওয়া যায়। 413 মানে বডির আকার nginx-এর client_max_body_size-এর চেয়ে বড়, যা ডিফল্টভাবে 1 megabyte, এবং এরর লগে এটি client intended to send too large body হিসেবে নিশ্চিত করা হয়।
স্ট্যাটিক ফাইলের ক্ষেত্রে 403 প্রায় সবসময়ই ফাইলসিস্টেমের সমস্যা, HTTP রুল নয়। কোনো কনফিগারেশন পরিবর্তনের আগে /var/log/nginx/error.log পড়ুন। open() "/srv/site/index.html" failed (13: Permission denied) মানে nginx ওয়ার্কার ইউজার ফাইলটি পড়তে পারছে না, সাধারণত এর কারণ হলো প্যারেন্ট ডিরেক্টরিতে অন্যদের জন্য execute পারমিশন নেই। directory index of "/srv/site/" is forbidden মানে পাথটি এমন একটি ডিরেক্টরিতে গিয়ে ঠেকেছে যেখানে কোনো ইনডেক্স ফাইল নেই এবং autoindex বন্ধ করা আছে।
5xx মানে আপনার সার্ভারের সমস্যা। 500 হলো সাধারণ সার্ভার এরর, এবং আপনার নিজের সাইটে এটি সাধারণত অ্যাপ্লিকেশনের কোনো আনহ্যান্ডেলড এরর নির্দেশ করে। 502 Bad Gateway মানে nginx আপস্ট্রিম থেকে কোনো ব্যবহারযোগ্য রেসপন্স পায়নি, এবং এরর লগ এর কারণ জানায়: connect() failed (111: Connection refused) while connecting to upstream মানে proxy_pass ঠিকানায় কোনো কিছু লিসেন করছে না। 504 Gateway Timeout মানে nginx আপস্ট্রিমের জন্য অপেক্ষা করা ছেড়ে দিয়েছে। সাধারণত আপস্ট্রিম কানেকশন গ্রহণ করার পর proxy_read_timeout-এর মধ্যে (ডিফল্ট 60 সেকেন্ড) কোনো উত্তর না দিলে এমন হয়, যা লগে upstream timed out (110: Connection timed out) while reading response header from upstream হিসেবে রেকর্ড হয়। কানেকশন প্রচেষ্টার সময় proxy_connect_timeout-এর মধ্যে (ডিফল্ট 60 সেকেন্ড) সম্পন্ন না হলে তা 504-এ শেষ হয় এবং লগের লাইনটি while connecting to upstream দিয়ে শেষ হয়। 503 Service Unavailable মানে সার্ভার সাময়িকভাবে অনুরোধটি হ্যান্ডেল করতে পারছে না, সাধারণত সার্ভার ওভারলোড হলে বা রক্ষণাবেক্ষণের জন্য বন্ধ থাকলে এমন হয়। মনে রাখবেন, nginx-এর নিজস্ব রেট লিমিটার 503 রিটার্ন করে, কারণ limit_req_status ডিফল্টভাবে 503 থাকে। আপনি যদি লগে 429 Too Many Requests খুঁজছেন কিন্তু তার বদলে 503 পাচ্ছেন, তবে এর কারণ এটিই। সঠিক কোড পেতে limit_req_status 429; সেট করুন।
সার্ভার চালানোর সময় গুরুত্বপূর্ণ header
Host সাইট নির্ধারণ করে। একটি IP address থেকে শত শত hostname পরিবেশন করা যায়। কোন server block উত্তর দেবে তা নির্ধারণ করতে nginx Host-কে server_name-এর সঙ্গে মিলিয়ে দেখে। কোনো কিছু না মিললে nginx default server ব্যবহার করে। অন্য কোনো block-কে default_server হিসেবে চিহ্নিত না করা থাকলে, ওই address ও port-এ listening করা প্রথম block-ই default server হয়। নতুন virtual host থেকে ভুল সাইট ফিরে আসার কারণ প্রায় সব সময় এটাই: নামটি মেলেনি, তাই অনুরোধটি default server-এ চলে গেছে। DNS পরিবর্তন না করেই এটি পরীক্ষা করুন:
curl -sS -o /dev/null -D - -H 'Host: app.example.com' http://127.0.0.1/User-Agent হলো client-এর দেওয়া নিজের পরিচয়, এবং এটি free text। log পড়ার সময় এটিকে শুধু একটি ইঙ্গিত হিসেবে ব্যবহার করুন। কখনো এটিকে নিয়ন্ত্রণের ভিত্তি করবেন না। কারণ কোনো client এটি সম্পর্কে মিথ্যা বলতে চাইলে তা সহজেই করতে পারে। তাই User-Agent দিয়ে scraper block করলে শুধু ভদ্রভাবে পরিচয় দেওয়া scraper-গুলোই বাদ পড়ে।
Content-Type byte কীভাবে ব্যাখ্যা করা হবে তা নির্ধারণ করে: API request-এর জন্য application/json এবং page-এর জন্য text/html; charset=utf-8। nginx /etc/nginx/mime.types ব্যবহার করে file extension-কে type-এর সঙ্গে মিলিয়ে দেয়। packaged nginx.conf default_type application/octet-stream; সেট করে। ফলে nginx যে extension চেনে না, সেই extension-যুক্ত file rendered না হয়ে download হিসেবে দেওয়া হয়। এর দৃশ্যমান লক্ষণ হলো styling ছাড়াই page load হওয়া, আর browser console-এ Refused to apply style from ... because its MIME type ('text/plain') is not a supported stylesheet MIME type দেখা। এখানে MIME বলতে multipurpose internet mail extensions বোঝায়। এই type string-গুলোর naming scheme এখান থেকেই এসেছে।
Cache-Control আপনার server এবং পাঠকের মাঝখানে থাকা প্রতিটি cache নিয়ন্ত্রণ করার পদ্ধতি। যে asset-এর filename-এ content hash আছে, তার জন্য public, max-age=31536000, immutable উপযোগী। কারণ content পরিবর্তিত হলে filename-ও পরিবর্তিত হয়। user-specific যেকোনো কিছুর জন্য no-store ব্যবহার করুন। কারণ logged in page ধরে রাখা shared cache একই URL-এর পরবর্তী অনুরোধকারীকে সেই page দিয়ে দিতে পারে। private হলো মাঝামাঝি setting। browser এটি সংরক্ষণ করতে পারে, কিন্তু shared cache পারে না।
X-Forwarded-For ব্যবহারের কারণ হলো proxy visitor-এর address আড়াল করে। কোনো request reverse proxy-এর মধ্য দিয়ে গেলে $remote_addr-এ proxy-এর address থাকে। তাই original address পুনরুদ্ধার না করা পর্যন্ত আপনার log, geolocation এবং rate limiting—সবকিছুই একই client দেখতে পায়। proxy-কে original address-টি সঙ্গে পাঠাতে হবে:
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;এরপর receiving server-কে এই address বিশ্বাস করতে বলতে হবে। একই সঙ্গে ঠিক কাকে বিশ্বাস করতে হবে সেটিও নির্দিষ্ট করতে হবে:
set_real_ip_from 10.0.0.0/8;
real_ip_header X-Forwarded-For;এই দুটি directive nginx-এর realip module-এর অন্তর্ভুক্ত। এটি $remote_addr-এর জায়গায় forwarded address বসায় এবং connection-এর নিজস্ব address $realip_remote_addr-এ রাখে। আপনি নিজে nginx compile করলে --with-http_realip_module যোগ করুন। কারণ module-টি default হিসেবে build করা হয় না।
শুধু আপনার নিয়ন্ত্রণাধীন range তালিকাভুক্ত করুন। X-Forwarded-For হলো plain text, যা যেকোনো client পাঠাতে পারে। তাই set_real_ip_from 0.0.0.0/0; ব্যবহার করলে visitor আপনার log-এ লেখা address এবং rate limiter-এ গণনা করা address—দুটিই নির্ধারণ করতে পারে।
X-Forwarded-Proto একটি নির্দিষ্ট এবং খুব সাধারণ সমস্যা প্রতিরোধ করে। আপনার proxy TLS termination করে এবং request-টি plain HTTP-তে application-এ পাঠায়। application plain request দেখতে পায়, visitor-কে HTTPS ব্যবহার করা উচিত বলে সিদ্ধান্ত নেয় এবং 301 https://example.com/ দিয়ে উত্তর দেয়। browser সেই redirect অনুসরণ করে। proxy আবার TLS termination করে plain HTTP পাঠায়। এভাবে browser ERR_TOO_MANY_REDIRECTS দিয়ে থেমে যাওয়া পর্যন্ত loop চলতে থাকে। X-Forwarded-Proto: https পাঠালে application বুঝতে পারে যে visitor ইতিমধ্যে HTTPS ব্যবহার করছে। ফলে এটি আর redirect করে না।
HTTP/1.1 বনাম HTTP/2 বনাম HTTP/3: আপনার জন্য কী পরিবর্তন হয়
HTTP/1.1 হলো টেক্সট-ভিত্তিক এবং বাস্তবে এটি প্রতি কানেকশনে একটি করে অনুরোধ (request) প্রসেস করে। Connection: keep-alive ব্যবহারের ফলে পরবর্তী অনুরোধগুলো একই TCP কানেকশন পুনরায় ব্যবহার করতে পারে, যা সেটআপের খরচ কমায়, কিন্তু অনুরোধের ক্রমানুসারে উত্তরগুলো ফিরে আসে। এই প্রোটোকলটি পাইপলাইনিং সমর্থন করে, যেখানে ক্লায়েন্ট উত্তরের জন্য অপেক্ষা না করেই একাধিক অনুরোধ পাঠাতে পারে, কিন্তু ক্রমানুসারের নিয়মটি তখনও কার্যকর থাকে এবং আধুনিক কোনো ব্রাউজার ডিফল্টভাবে পাইপলাইনিং চালু রাখে না। একটি ধীরগতির উত্তর তার পেছনে থাকা সব অনুরোধকে আটকে দেয়। একে বলা হয় head-of-line blocking, এবং ব্রাউজারগুলো একই হোস্টনামে একসাথে একাধিক কানেকশন খুলে এই সমস্যা এড়ানোর চেষ্টা করে।
HTTP/2 একই মেথড এবং স্ট্যাটাস কোড বজায় রাখে, কিন্তু ফ্রেমগুলোকে বাইনারিতে রূপান্তর করে। অনেকগুলো অনুরোধ একটি কানেকশনকে স্বতন্ত্র স্ট্রিম হিসেবে শেয়ার করে এবং বারবার আসা হেডার টেক্সট কম্প্রেস করা হয়, যা গুরুত্বপূর্ণ কারণ আধুনিক অনুরোধে প্রচুর হেডার থাকে। কানেকশনটি এখনও TCP, তাই একটি প্যাকেট হারিয়ে গেলে সেই কানেকশনের সব স্ট্রিম রিট্রান্সমিশন না আসা পর্যন্ত আটকে থাকে। head-of-line blocking দূর হয়নি, এটি HTTP স্তর থেকে ট্রান্সপোর্ট স্তরে নেমে এসেছে। সার্ভার পুশ HTTP/2-এর অংশ ছিল, কিন্তু বাস্তবে এটি এখন আর নেই, কারণ 2022 সালে Chrome এর সমর্থন তুলে নিয়েছে।
HTTP/3 আবারও একই সেমান্টিকস বজায় রাখে এবং TCP-কে QUIC দিয়ে প্রতিস্থাপন করে, যা UDP (user datagram protocol)-এর ওপর ভিত্তি করে তৈরি একটি ট্রান্সপোর্ট। QUIC স্ট্রিমগুলো সম্পূর্ণ স্বতন্ত্র, তাই একটি প্যাকেট হারিয়ে গেলে শুধুমাত্র সংশ্লিষ্ট স্ট্রিমটিই আটকে থাকে। TLS 1.3 সরাসরি QUIC হ্যান্ডশেকের ভেতরেই থাকে, আলাদা কোনো স্তরে নয়, তাই নতুন কানেকশনে কম রাউন্ড ট্রিপের প্রয়োজন হয়। এর দুটি ব্যবহারিক ফলাফল রয়েছে: আপনার সার্ভার HTTP/3-এর জন্য যে UDP পোর্ট ব্যবহার করে তা পথের প্রতিটি ফায়ারওয়ালে খোলা থাকতে হবে, এবং যে নেটওয়ার্ক UDP ব্লক বা থ্রটল করে তা ক্লায়েন্টদের HTTP/2-তে ফিরিয়ে নেবে। এই পোর্টটি সাধারণত 443 হয়, তবে প্রোটোকলটি সার্ভারকে যেকোনো UDP পোর্ট ব্যবহারের অনুমতি দেয়, কারণ ব্রাউজারকে HTTP/3 সম্পর্কে জানানোর সময় পোর্টটি স্পষ্টভাবে উল্লেখ করা থাকে।
আপনার জন্য বাস্তবে কী পরিবর্তন হয়। ব্রাউজারগুলো সাধারণত HTTP/3 দিয়ে শুরু করে না। তারা HTTP/2 বা HTTP/1.1 দিয়ে কানেক্ট করে, রেসপন্সে একটি Alt-Svc: h3=":443"; ma=86400 হেডার দেখে এবং পরবর্তী কানেকশনের জন্য HTTP/3 ব্যবহার করে। এর ব্যতিক্রম হলো DNS-এ একটি HTTPS রেকর্ড যার alpn ভ্যালুতে h3 তালিকাভুক্ত থাকে, যা Firefox-এর মতো ব্রাউজারকে প্রথম কানেকশন থেকেই HTTP/3 খোলার সুযোগ দেয়। সেই রেকর্ডটি একটি অতিরিক্ত ইঙ্গিত, কোনো বিকল্প নয়, তাই হেডারটি ঐচ্ছিক কোনো সাজসজ্জা নয়। এটিই মূল আবিষ্কারের মাধ্যম। nginx-এ, HTTP/2 সংস্করণ 1.25.1-এ নিজস্ব ডিরেক্টিভ হয়ে ওঠে (server ব্লকের ভেতরে http2 on;, যা পুরনো listen ... http2 প্যারামিটারকে প্রতিস্থাপন করে), এবং QUIC আসে mainline 1.25.0-এ, যেখানে একটি HTTP/3 সাইটের জন্য সাধারণ listen 443 ssl;-এর পাশাপাশি listen 443 quic reuseport; প্রয়োজন হয়। HTTP/3 মডিউলটি ডিফল্টভাবে তৈরি করা থাকে না (--with-http_v3_module), এবং nginx-এর ডকুমেন্টেশন এখনও এর সমর্থনকে পরীক্ষামূলক বলে উল্লেখ করে।
প্রক্সিগুলোর পরিপক্কতা এখানে ভিন্ন, তাই আপনি যে সংস্করণটি চালাচ্ছেন তার সাথে এটি মিলিয়ে দেখা জরুরি। নভেম্বর 2026 অনুযায়ী, Caddy ডিফল্টভাবে কোনো কনফিগারেশন ছাড়াই HTTP/3 পরিবেশন করে। nginx-এর জন্য উপরে বর্ণিত Alt-Svc হেডারসহ একটি স্পষ্ট quic লিসেনার প্রয়োজন। Traefik একটি স্পষ্ট http3 অপশনের মাধ্যমে প্রতিটি এন্ট্রি পয়েন্টে এটি চালু করে। আপনি যদি Traefik in front of several Docker apps-এ TLS টার্মিনেট করেন, তবে আপনার ভিজিটররা কোন প্রোটোকল সংস্করণ পাবে তা সেখানেই নির্ধারিত হয়, এবং প্রক্সি থেকে আপনার কন্টেইনারের হপটি সাধারণত সাধারণ HTTP/1.1 হয়, ব্রাউজার যা-ই নেগোশিয়েট করুক না কেন।
অনুমান না করে যাচাই করুন। curl --http3 -sS -o /dev/null -D - https://example.com/ শুধুমাত্র তখনই কাজ করে যদি curl -V-এর ফিচারের তালিকায় HTTP3 থাকে, এবং বেশিরভাগ ডিস্ট্রিবিউশন বিল্ডে এটি অন্তর্ভুক্ত থাকে না। নির্ভরযোগ্য পরীক্ষা হলো আপনার নিজস্ব লগ: ফরম্যাটে $server_protocol যোগ করুন এবং দেখুন আসল ব্রাউজারগুলো কী নেগোশিয়েট করছে। এর আগে, নিশ্চিত করুন যে আপনার quic লিসেনার যে UDP পোর্টটি ব্যবহার করছে (উপরের কনফিগারেশনে 443), তা আসলে খোলা আছে কি না। কারণ শুধুমাত্র TCP 443 অনুমতি দেয় এমন ফায়ারওয়াল HTTP/3-কে নীরবে ব্যর্থ হতে দেবে, যখন সাইটটি HTTP/2-এর মাধ্যমে কাজ চালিয়ে যাবে। which ports are open and listening on your Linux server তা জানা হলো প্রথম কাজ।
HTTPS: HTTP হলো প্রোটোকল, TLS হলো র্যাপার
HTTPS কোনো আলাদা প্রোটোকল নয়। এটি মূলত TLS সেশনের ভেতরে আদান-প্রদান করা একই HTTP রিকোয়েস্ট এবং স্ট্যাটাস কোড। পোর্ট 80-এ এগুলো প্লেইন টেক্সটে থাকে এবং পোর্ট 443-এ এগুলো এনক্রিপ্ট করা অবস্থায় থাকে। TLS হ্যান্ডশেক প্রথমে সম্পন্ন হয়, তারপর এনক্রিপ্ট করা চ্যানেলের ভেতর দিয়ে HTTP রিকোয়েস্ট পাঠানো হয়। এই ক্রমের কারণেই কোনো সার্টিফিকেট সমস্যায় কখনোই কোনো স্ট্যাটাস কোড থাকে না: একটি HTTP বাইট পাঠানোর আগেই ব্যর্থতা ঘটে, তাই কোনো রেসপন্স কোড দেওয়ার সুযোগ থাকে না।
একাধিক সাইট হোস্ট করা সার্ভারের ক্ষেত্রে একটি ক্রম গুরুত্বপূর্ণ। সার্টিফিকেট নির্বাচন করা হয় SNI (server name indication) ব্যবহার করে, যা TLS হ্যান্ডশেকের একটি ফিল্ড। কোনো HTTP হেডার তৈরি হওয়ার আগেই এটি হোস্টনেমকে প্লেইন টেক্সটে বহন করে। তাই সার্ভার প্রথমে SNI থেকে সার্টিফিকেট বেছে নেয়, তারপর দ্বিতীয় ধাপে Host হেডার থেকে ভার্চুয়াল হোস্ট নির্বাচন করে। এগুলো দুটি আলাদা লুকআপ যা সাধারণত একই ফলাফল দেয়। যখন তারা একমত হয় না, তখন ব্রাউজার NET::ERR_CERT_COMMON_NAME_INVALID-এর মতো একটি নেম মিসম্যাচ এরর দেখায় এবং কোনো রিকোয়েস্ট পাঠায় না, কারণ আপনার ডিফল্ট সার্ভারের সার্টিফিকেট এমন একটি নামের জন্য দেওয়া হয়েছিল যা এটি কভার করে না।
পাবলিক সাইটের জন্য একটি আসল সার্টিফিকেট নিন এবং সেটিকে স্বয়ংক্রিয়ভাবে রিনিউ হতে দিন। nginx-এ Let's Encrypt সহ Certbot আপনার সার্ভার ব্লকে সার্টিফিকেটের পাথ লিখে দেয় এবং আপনার জন্য রিনিউয়াল টাইমার ইনস্টল করে। এমন কোনো হোস্টনেমের জন্য যা কোনো পাবলিক অথরিটি ভেরিফাই করতে পারে না, যেমন ইন্টারনাল নাম বা আপনার নিজস্ব নেটওয়ার্কের কোনো বেয়ার IP অ্যাড্রেস, Ubuntu-তে self-signed সার্টিফিকেট ব্যবহার করা একটি সৎ উপায়, তবে আপনাকে মেনে নিতে হবে যে প্রতিটি ক্লায়েন্টকে এটি বিশ্বাস করার জন্য নির্দেশ দিতে হবে।
একবার TLS কাজ শুরু করলে, পোর্ট 80-এর সব ট্রাফিক পোর্ট 443-এ পাঠিয়ে দিন:
server {
listen 80;
server_name example.com;
return 301 https://$host$request_uri;
}শুধুমাত্র নিশ্চিত হওয়ার পরেই Strict-Transport-Security যোগ করুন। add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always; হেডারটি ব্রাউজারকে নির্দেশ দেয় যেন তারা দুই বছরের জন্য ওই হোস্টনেমের ক্ষেত্রে প্লেইন HTTP প্রত্যাখ্যান করে, এবং ব্রাউজার তাদের নিজস্ব ক্যাশ থেকে এটি মেনে চলে। এর মানে হলো, পরবর্তীতে হেডারটি সরিয়ে ফেললেও তা আর আগের অবস্থায় ফিরে আসে না। প্রথমে কয়েক ঘণ্টার জন্য একটি max-age দিয়ে শুরু করুন, নিশ্চিত করুন যে প্রতিটি সাবডোমেইন সত্যিই HTTPS-এ আছে, তারপর এর সময়সীমা বাড়ান।
FAQ
HTTP এবং HTTPS-এর মধ্যে পার্থক্য কী?
HTTPS হলো TLS (transport layer security) সেশনের ভেতরে পাঠানো HTTP। এর মেথড এবং স্ট্যাটাস কোডগুলো একই। পার্থক্য হলো, ক্লায়েন্ট এবং যে প্রান্ত TLS টার্মিনেট করছে, তাদের মধ্যে ডেটা এনক্রিপ্ট করা থাকে এবং ডিফল্ট পোর্ট 80 থেকে পরিবর্তিত হয়ে 443 হয়। যেহেতু প্রথম HTTP বাইট পাঠানোর আগেই TLS হ্যান্ডশেক সম্পন্ন হয়, তাই সার্টিফিকেটের কোনো ত্রুটি কখনোই স্ট্যাটাস কোড তৈরি করে না। এই কারণেই ব্রাউজারের সার্টিফিকেট ওয়ার্নিংয়ে 403-এর মতো কোনো সংখ্যার পরিবর্তে NET::ERR_CERT_COMMON_NAME_INVALID-এর মতো এরর নাম দেখানো হয়।
আমার সাইট কেন 502 Bad Gateway রিটার্ন করছে?
Nginx থেকে আসা 502 মানে হলো, Nginx তার আপস্ট্রিম থেকে কোনো ব্যবহারযোগ্য রেসপন্স পায়নি। অর্থাৎ ভিজিটরের অনুরোধ ঠিক ছিল, কিন্তু Nginx-এর পেছনের কোনো কিছুতে সমস্যা আছে। /var/log/nginx/error.log পড়ুন। connect() failed (111: Connection refused) while connecting to upstream মানে হলো proxy_pass-এ উল্লিখিত অ্যাড্রেস এবং পোর্টে কোনো সার্ভিস লিসেন করছে না। তাই অ্যাপ্লিকেশনটি চলছে কি না এবং প্রত্যাশিত ঠিকানায় বাইন্ড করা আছে কি না তা পরীক্ষা করুন। no live upstreams while connecting to upstream মানে হলো, বারবার ব্যর্থ হওয়ার পর আপস্ট্রিম ব্লকের প্রতিটি সার্ভারকে ডাউন হিসেবে চিহ্নিত করা হয়েছে। 504 Gateway Timeout-এর সাথে তুলনা করুন, যার অর্থ হলো আপস্ট্রিমের জন্য অপেক্ষা করতে গিয়ে Nginx টাইমআউট হয়েছে; এটি হতে পারে proxy_connect_timeout-এর মধ্যে কানেকশন সম্পন্ন না হওয়ায় অথবা proxy_read_timeout-এর মধ্যে রেসপন্স না পাওয়ায়।
আমার এক্সেস লগে কেন প্রতিটি ভিজিটরের জন্য একই IP অ্যাড্রেস দেখাচ্ছে?
কারণ $remote_addr সেই অ্যাড্রেসটি রেকর্ড করে যা TCP কানেকশন ওপেন করেছে। রিভার্স প্রক্সি বা কন্টেন্ট ডেলিভারি নেটওয়ার্কের পেছনে থাকলে সেই অ্যাড্রেসটি প্রক্সির অ্যাড্রেস হয়। ভিজিটরের আসল অ্যাড্রেসটি X-Forwarded-For হেডারে থাকে। প্রক্সিতে proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; সেট করুন, তারপর রিসিভিং Nginx-এ set_real_ip_from-কে প্রক্সির অ্যাড্রেস রেঞ্জে সেট করুন এবং real_ip_header X-Forwarded-For; ব্যবহার করুন। শুধুমাত্র আপনার নিয়ন্ত্রণে থাকা রেঞ্জগুলোই তালিকায় রাখুন, কারণ এই হেডারটি যেকোনো ক্লায়েন্ট পাঠাতে পারে। পুরো ইন্টারনেটের জন্য এটি বিশ্বাস করলে যেকোনো ভিজিটর আপনার লগ করা অ্যাড্রেস এবং রেট লিমিট করার অ্যাড্রেস পরিবর্তন করে দিতে পারে।
আমার কি HTTP/2 বা HTTP/3 চালু করা প্রয়োজন?
HTTP/2 চালু করা লাভজনক, কারণ TLS আছে এমন সাইটে এটি মাত্র একটি ডিরেক্টিভ এবং এটি প্রতি কানেকশনে অনুরোধের সীমাবদ্ধতা দূর করে, যা অনেক ছোট ফাইল থাকা পেজকে দ্রুত লোড হতে সাহায্য করে। HTTP/3-এর সুবিধা তুলনামূলক কম এবং অনিশ্চিত, তাছাড়া এর জন্য একটি ওপেন UDP পোর্ট (সাধারণত 443) এবং QUIC সাপোর্টসহ প্রক্সি বিল্ড প্রয়োজন। মনে রাখবেন, ব্রাউজার সাধারণত তখনই HTTP/3-তে সুইচ করে যখন তারা আগের কোনো রেসপন্সে Alt-Svc হেডার দেখে অথবা এমন কোনো HTTPS DNS রেকর্ড পায় যেখানে h3 তালিকাভুক্ত থাকে। তাই এগুলোর কোনোটি ছাড়া আপনার listen লাইনে যা-ই থাকুক না কেন, কোনো পরিবর্তন হবে না। আপনার লগ ফরম্যাটে $server_protocol যোগ করুন এবং সময় ব্যয় করার আগে ভিজিটররা আসলে কী নেগোশিয়েট করছে তা পরিমাপ করুন।
ফাইল থাকা সত্ত্বেও 403 Forbidden মানে কী?
স্ট্যাটিক সাইটের ক্ষেত্রে 403 সাধারণত HTTP রুল নয়, বরং ফাইলসিস্টেম পারমিশনের সমস্যা। open() ... failed (13: Permission denied)-এর ক্ষেত্রে /var/log/nginx/error.log মানে হলো Nginx ওয়ার্কার ইউজার ফাইলটি পড়তে পারছে না। এটি সাধারণত ফাইল মোডের সমস্যার চেয়ে প্যারেন্ট ডিরেক্টরিতে অন্যদের জন্য এক্সিকিউট পারমিশন না থাকার কারণে বেশি হয়। directory index of ... is forbidden মানে হলো অনুরোধটি এমন একটি ডিরেক্টরিতে গেছে যেখানে কোনো ইনডেক্স ফাইল নেই এবং autoindex বন্ধ আছে। ম্যাচিং location ব্লকে কোনো স্পষ্ট deny রুল থাকলেও 403 রিটার্ন করে। তাই এরর লগে কিছু না থাকলে সেই ব্লকটি পড়ুন।