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ব্রাউজার এবং মধ্যবর্তী যেকোনো ক্যাশকে জানায় যে তারা কতক্ষণ এই প্রতিক্রিয়াটি সংরক্ষণ করতে পারবে।- হেডারগুলোর পরের খালি লাইনটি উভয় ক্ষেত্রেই হেডার এবং বডিকে আলাদা করে।
হেডার নামগুলো 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"';এর মাধ্যমে তৈরি একটি লাইন:
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, অর্থাৎ যে ঠিকানা থেকে TCP (transmission control protocol) connection খোলা হয়েছে। প্রক্সির পেছনে থাকলে এটি প্রক্সির ঠিকানা দেখাবে, ভিজিটরের নয়।- প্রথম
-একটি নির্দিষ্ট placeholder। দ্বিতীয়টি হলো$remote_user, যা শুধুমাত্র HTTP basic authentication ব্যবহৃত হলেই পূর্ণ হয়। "GET /pricing HTTP/1.1"হলো$request, অর্থাৎ request line-টি ঠিক যেভাবে এসেছে সেভাবেই কপি করা হয়েছে।200হলো আপনার সার্ভার থেকে ফেরত পাঠানো status, ভিজিটর যা দেখেছে তা নয়।5310হলো$body_bytes_sent, শুধুমাত্র body-এর আকার। Response header-এর আকার এতে গণনা করা হয় না, তাই এই সংখ্যাটি প্রকৃতপক্ষে পাঠানো bytes-এর চেয়ে সবসময় কম হয়।- উদ্ধৃতি চিহ্নের ভেতর থাকা শেষ দুটি ফিল্ড হলো
RefererএবংUser-Agent। উভয়ই ক্লায়েন্ট থেকে আসে, তাই এগুলোতে যেকোনো কিছু থাকতে পারে।
যেহেতু $request হুবহু কপি করা হয়, তাই আবর্জনা বা ভুল তথ্যও হুবহু দেখা যায়। কোনো ক্লায়েন্ট যদি আপনার plaintext port 80-এ TLS (transport layer security) ব্যবহার করে কথা বলে, তবে সেটি একটি 400 লাইন তৈরি করবে যার request ফিল্ডটি "\x16\x03\x01\x02\x00\x01"-এর মতো escaped bytes দিয়ে শুরু হয়। \x16 হলো TLS handshake record type, তাই এই bytes-গুলো আসলে একটি ClientHello-এর শুরু, কোনো request line নয়। আপনার সার্ভার সঠিকভাবে কাজ করছে। কেউ হয়তো HTTPS-কে ভুলবশত একটি HTTP পোর্টে পাঠাচ্ছে।
আপনার log format-এ $server_protocol যোগ করুন। এটি HTTP/1.1, HTTP/2.0 অথবা HTTP/3.0 প্রিন্ট করে, এবং প্রোটোকল পরিবর্তন কার্যকর হয়েছে কি না তা প্রমাণ করার এটিই দ্রুততম উপায়।
আপনার নিজস্ব সাইট থেকে আসা সাধারণ স্ট্যাটাস কোডগুলোর অর্থ
প্রথম সংখ্যাটি হলো ক্লাসের নির্দেশক, এবং ক্লাসটিই আপনার সবার আগে দেখা উচিত।
2xx মানে সবকিছু ঠিকঠাক কাজ করেছে। সাধারণ রিডের জন্য 200 OK। কোনো কিছু তৈরি করার পর POST-এর প্রতিক্রিয়ায় 201 Created। কোনো কিছু ফেরত পাঠানোর প্রয়োজন নেই এমন সফলতার ক্ষেত্রে 204 No Content, যা সাধারণত DELETE-এর সাধারণ উত্তর।
3xx মানে অন্য কোথাও দেখুন। 301 হলো স্থায়ী এবং ব্রাউজারগুলো একে শক্তভাবে ক্যাশ করে রাখে, অনেক সময় ব্যবহারকারী প্রোফাইল ক্লিয়ার না করা পর্যন্ত এটি থেকে যায়, তাই ভুল হোস্টনামের দিকে নির্দেশ করা একটি 301 ঠিক করা বেশ কষ্টসাধ্য। রিডাইরেক্ট পরীক্ষা করার সময় 302 ব্যবহার করুন। 304 Not Modified কোনো ত্রুটি নয়, বরং একটি সফলতা: ক্লায়েন্ট এমন একটি If-None-Match পাঠিয়েছে যাতে একটি ETag (entity tag) আছে যা আপনি এখনো শনাক্ত করতে পারছেন, তাই আপনি বডি ছাড়া শুধু হেডার দিয়ে উত্তর দিয়েছেন। লগ ভর্তি 304 থাকা মানে ক্যাশিং সঠিকভাবে কাজ করছে।
4xx মানে অনুরোধটি ভুল ছিল। 400 Bad Request মানে ইনপুট ভুলভাবে দেওয়া হয়েছে। 401 Unauthorized মানে আসলে প্রমাণীকরণ বা অথেন্টিকেশন করা হয়নি, এবং এতে অবশ্যই একটি WWW-Authenticate হেডার থাকতে হবে যা স্কিমটির নাম উল্লেখ করবে। 403 Forbidden মানে অনুরোধটি বোঝা গেছে কিন্তু তারপরও তা প্রত্যাখ্যান করা হয়েছে। 404 Not Found মানে এমন একটি পাথ যা অস্তিত্বহীন। 405 Method Not Allowed মানে পাথ সঠিক কিন্তু মেথড ভুল, যা স্ট্যাটিক ফাইল লোকেশনে POST করলে ফেরত আসে। 413 মানে বডিটি Nginx-এর client_max_body_size-এর চেয়ে বড়, যার ডিফল্ট মান 1 মেগাবাইট, এবং এরর লগ client intended to send too large body দিয়ে তা নিশ্চিত করে।
স্ট্যাটিক ফাইলের ক্ষেত্রে 403 প্রায় সবসময়ই HTTP রুলের চেয়ে ফাইলসিস্টেমের সমস্যার কারণে হয়। কোনো কনফিগারেশন পরিবর্তন করার আগে /var/log/nginx/error.log পড়ুন। open() "/srv/site/index.html" failed (13: Permission denied) মানে Nginx ওয়ার্কার ইউজার ফাইলটি পড়তে পারছে না, সাধারণত এর কারণ হলো প্যারেন্ট ডিরেক্টরিতে অন্যদের জন্য এক্সিকিউট পারমিশন নেই। 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 মানে আপস্ট্রিম কানেকশন গ্রহণ করেছে কিন্তু proxy_read_timeout-এর মধ্যে (ডিফল্ট 60 সেকেন্ড) কোনো উত্তর দেয়নি, যা লগ upstream timed out (110: Connection timed out) while reading response header from upstream হিসেবে রেকর্ড করে। 503 Service Unavailable মানে ইচ্ছাকৃতভাবে প্রত্যাখ্যান করা হয়েছে। মনে রাখবেন, Nginx-এর নিজস্ব রেট লিমিটার 503 ফেরত দেয়, কারণ limit_req_status-এর ডিফল্ট মান 503। আপনি যদি লগে 429 Too Many Requests খুঁজছেন কিন্তু তার বদলে 503 পাচ্ছেন, তবে এর কারণ এটিই। সঠিক কোড পেতে limit_req_status 429; সেট করুন।
সার্ভার চালানোর সময় যে হেডারগুলো গুরুত্বপূর্ণ
Host সাইট নির্বাচন করে। একটি IP address শত শত hostname পরিবেশন করতে পারে এবং nginx কোন server ব্লকটি অনুরোধের উত্তর দেবে তা নির্ধারণ করতে Host-কে server_name-এর সাথে মিলিয়ে দেখে। যদি কোনোটির সাথে না মেলে, তবে nginx ডিফল্ট সার্ভার ব্যবহার করে। এটি সেই নির্দিষ্ট address এবং port-এ থাকা প্রথম ব্লক, যদি না অন্য কোনোটিকে default_server হিসেবে চিহ্নিত করা থাকে। নতুন virtual host থেকে ভুল সাইট আসার কারণ প্রায় সবসময়ই এটি: নাম না মেলায় অনুরোধটি ডিফল্ট সার্ভারে চলে গেছে। DNS পরিবর্তন না করেই এটি পরীক্ষা করুন:
curl -sS -o /dev/null -D - -H 'Host: app.example.com' http://127.0.0.1/User-Agent হলো ক্লায়েন্টের লেখা একটি স্ব-বর্ণনা এবং এটি মুক্ত টেক্সট। লগ পড়ার সময় এটিকে একটি ইঙ্গিত হিসেবে ব্যবহার করুন। এটিকে কখনোই নিয়ন্ত্রণের মাধ্যম হিসেবে ব্যবহার করবেন না, কারণ যে ক্লায়েন্ট মিথ্যা বলতে চায় সে সহজেই তা পারে। তাই User-Agent দিয়ে কোনো স্ক্র্যাপারকে ব্লক করলে কেবল ভদ্র স্ক্র্যাপাররাই ফিল্টার হয়।
Content-Type নির্ধারণ করে বাইটগুলো কীভাবে ব্যাখ্যা করা হবে: API অনুরোধের জন্য application/json, পৃষ্ঠার জন্য text/html; charset=utf-8। nginx ফাইল এক্সটেনশনকে /etc/nginx/mime.types-এর মাধ্যমে টাইপের সাথে ম্যাপ করে এবং প্যাকেজ করা nginx.conf ফাইলটি default_type application/octet-stream; সেট করে। তাই nginx যে এক্সটেনশন চেনে না, তা রেন্ডার না হয়ে ডাউনলোড হিসেবে অফার করা হয়। এর দৃশ্যমান লক্ষণ হলো এমন একটি পৃষ্ঠা যা কোনো স্টাইলিং ছাড়াই লোড হয় এবং ব্রাউজার কনসোলে Refused to apply style from ... because its MIME type ('text/plain') is not a supported stylesheet MIME type প্রিন্ট হয়। MIME হলো multipurpose internet mail extensions, যা সেই টাইপ স্ট্রিংগুলোর নামকরণের পদ্ধতি।
Cache-Control হলো আপনার সার্ভার এবং পাঠকের মধ্যবর্তী প্রতিটি ক্যাশ নিয়ন্ত্রণ করার উপায়। public, max-age=31536000, immutable সেই সব অ্যাসেটের জন্য উপযুক্ত যেগুলোর ফাইলের নামে content hash থাকে, কারণ বিষয়বস্তু পরিবর্তিত হলে নামও পরিবর্তিত হয়। no-store ব্যবহারকারী-নির্দিষ্ট যেকোনো কিছুর জন্য প্রযোজ্য, কারণ একটি শেয়ার্ড ক্যাশ যদি লগ-ইন করা পৃষ্ঠা রেখে দেয়, তবে একই URL চাওয়া পরবর্তী ব্যক্তিকে সেটি দেখিয়ে দিতে পারে। private হলো মধ্যবর্তী সেটিং: ব্রাউজার এটি রাখতে পারে, কিন্তু শেয়ার্ড ক্যাশ তা পারবে না।
X-Forwarded-For এর অস্তিত্বের কারণ হলো প্রক্সি ভিজিটরকে লুকিয়ে রাখে। একবার একটি অনুরোধ reverse proxy-এর মধ্য দিয়ে গেলে, $remote_addr হয় প্রক্সির ঠিকানা। ফলে আপনার লগ, জিওলোকেশন এবং রেট লিমিটিং কেবল একটি ক্লায়েন্টকেই দেখতে পায়। প্রক্সিকে অবশ্যই মূল ঠিকানাটি পাঠিয়ে দিতে হয়:
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;এরপর রিসিভিং সার্ভারকে এটি বিশ্বাস করতে এবং ঠিক কাকে বিশ্বাস করতে হবে তা বলে দিতে হয়:
set_real_ip_from 10.0.0.0/8;
real_ip_header X-Forwarded-For;শুধুমাত্র আপনার নিয়ন্ত্রণে থাকা রেঞ্জগুলোই তালিকাভুক্ত করুন। X-Forwarded-For হলো সাধারণ টেক্সট যা যেকোনো ক্লায়েন্ট পাঠাতে পারে, তাই set_real_ip_from 0.0.0.0/0; একজন ভিজিটরকে সেই ঠিকানা বেছে নেওয়ার সুযোগ দেয় যা আপনি লগ করেন এবং যা আপনার রেট লিমিটার গণনা করে।
X-Forwarded-Proto একটি নির্দিষ্ট এবং খুব সাধারণ ব্যর্থতা প্রতিরোধ করে। আপনার প্রক্সি TLS টার্মিনেট করে এবং plain HTTP-এর মাধ্যমে অ্যাপ্লিকেশনটিতে অনুরোধ পাঠায়। অ্যাপ্লিকেশনটি একটি সাধারণ অনুরোধ দেখে, সিদ্ধান্ত নেয় যে ভিজিটরের HTTPS-এ থাকা উচিত এবং 301 https://example.com/ উত্তর দেয়। ব্রাউজার তা অনুসরণ করে, প্রক্সি আবার TLS টার্মিনেট করে এবং আবার plain HTTP পাঠায়। ব্রাউজার ERR_TOO_MANY_REDIRECTS দিয়ে হাল না ছাড়া পর্যন্ত এই লুপ চলতেই থাকে। X-Forwarded-Proto: https পাঠানো অ্যাপ্লিকেশনটিকে জানায় যে ভিজিটর ইতিমধ্যেই HTTPS-এ আছে, তাই এটি রিডাইরেক্ট করা বন্ধ করে দেয়।
HTTP/1.1 বনাম HTTP/2 বনাম HTTP/3: আপনার জন্য কী পরিবর্তন হয়
HTTP/1.1 হলো টেক্সট-ভিত্তিক এবং এটি প্রতি connection-এ একটি করে অনুরোধ পরিচালনা করে। Connection: keep-alive পরবর্তী অনুরোধের জন্য একই TCP connection পুনরায় ব্যবহারের সুযোগ দেয়, যা setup cost কমায়, কিন্তু অনুরোধের ক্রমানুসারে সাড়া (response) ফিরে আসে। একটি ধীরগতির সাড়া তার পেছনে থাকা সব অনুরোধকে আটকে রাখে। একে বলা হয় head-of-line blocking, এবং ব্রাউজারগুলো একই hostname-এ একসাথে একাধিক connection খুলে এই সমস্যা এড়ানোর চেষ্টা করে।
HTTP/2 একই method এবং status code বজায় রাখে, কিন্তু framing-কে binary-তে রূপান্তর করে। অনেকগুলো অনুরোধ একটি connection-কে স্বাধীন stream হিসেবে ভাগ করে নেয় এবং বারবার আসা header টেক্সট সংকুচিত (compress) করা হয়, যা আধুনিক অনুরোধের ক্ষেত্রে গুরুত্বপূর্ণ কারণ এতে প্রচুর header থাকে। connection-টি এখনো TCP, তাই একটি packet হারিয়ে গেলে retransmission না আসা পর্যন্ত ওই connection-এর সব stream আটকে থাকে। head-of-line blocking দূর হয়নি, বরং এটি HTTP স্তর থেকে transport স্তরে নেমে এসেছে। Server push HTTP/2-এর অংশ ছিল কিন্তু বাস্তবে এটি এখন আর নেই, কারণ 2022 সালে Chrome এর সমর্থন তুলে নিয়েছে।
HTTP/3 পুনরায় একই semantics বজায় রাখে এবং TCP-কে QUIC দিয়ে প্রতিস্থাপন করে, যা UDP (user datagram protocol)-এর ওপর নির্মিত একটি transport। QUIC streamগুলো সম্পূর্ণ স্বাধীন, তাই একটি packet হারিয়ে গেলে শুধুমাত্র সংশ্লিষ্ট stream-টিই আটকে থাকে। TLS 1.3-কে আলাদা স্তরে না রেখে QUIC handshake-এর ভেতরেই যুক্ত করা হয়েছে, ফলে নতুন connection-এর জন্য কম round trip প্রয়োজন হয়। এর দুটি ব্যবহারিক ফলাফল রয়েছে: প্রতিটি firewall-এ UDP port 443 অবশ্যই খোলা থাকতে হবে এবং যে নেটওয়ার্ক UDP ব্লক বা সীমিত করে, তা client-কে HTTP/2-তে ফিরিয়ে নিয়ে যাবে।
আপনার জন্য সুনির্দিষ্টভাবে কী পরিবর্তন হয়। ব্রাউজার কখনোই সরাসরি HTTP/3 দিয়ে শুরু করে না। তারা HTTP/2 বা HTTP/1.1 দিয়ে connection তৈরি করে, response-এ একটি Alt-Svc: h3=":443"; ma=86400 header দেখে এবং পরবর্তী connection-এর জন্য HTTP/3 ব্যবহার করে। তাই এই header কোনো ঐচ্ছিক বিষয় নয়, বরং এটি discovery mechanism। nginx-এ, version 1.25.1 থেকে HTTP/2 একটি নিজস্ব directive হয়েছে (server ব্লকের ভেতরে http2 on;, যা পুরনো listen ... http2 parameter-কে প্রতিস্থাপন করেছে), এবং QUIC এসেছে mainline 1.25.0-তে, যেখানে একটি HTTP/3 সাইটের জন্য সাধারণ listen 443 ssl;-এর পাশাপাশি listen 443 quic reuseport; প্রয়োজন।
Proxy-গুলোর পরিপক্কতা এখানে ভিন্ন, তাই আপনি যে version ব্যবহার করছেন তার সাথে এটি মিলিয়ে দেখা জরুরি। আগস্ট 2026 অনুযায়ী, Caddy কোনো configuration ছাড়াই ডিফল্টভাবে HTTP/3 পরিবেশন করে। nginx-এর জন্য স্পষ্ট quic listener এবং উপরে বর্ণিত Alt-Svc header প্রয়োজন। Traefik একটি স্পষ্ট http3 option-এর মাধ্যমে প্রতি entry point-এ এটি সক্রিয় করে। আপনি যদি Traefik in front of several Docker apps-এ TLS termination করেন, তবে আপনার ভিজিটররা কোন protocol version পাবে তা সেখানেই নির্ধারিত হয়, এবং proxy থেকে আপনার container-এর মধ্যকার hop সাধারণত plain HTTP/1.1 হয়, ব্রাউজার যা-ই negotiate করুক না কেন।
অনুমান না করে যাচাই করুন। curl --http3 -sS -o /dev/null -D - https://example.com/ শুধুমাত্র তখনই কাজ করে যদি curl -V-এর features-এর তালিকায় HTTP3 থাকে, এবং বেশিরভাগ distribution build-এ এটি অন্তর্ভুক্ত থাকে না। নির্ভরযোগ্য পরীক্ষা হলো আপনার নিজের log: format-এ $server_protocol যোগ করুন এবং দেখুন প্রকৃত ব্রাউজারগুলো কী negotiate করছে। এর আগে নিশ্চিত করুন যে UDP 443 সত্যিই খোলা আছে, কারণ যে firewall শুধুমাত্র 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-এর সাথে তুলনা করুন, যার অর্থ হলো আপস্ট্রিম কানেকশন গ্রহণ করেছিল কিন্তু 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 হেডার দেখতে পায়। তাই সেই হেডার ছাড়া আপনার listen লাইনে যা-ই লেখা থাকুক না কেন, কোনো পরিবর্তন হবে না। আপনার লগ ফরম্যাটে $server_protocol যোগ করুন এবং সময় ব্যয় করার আগে ভিজিটররা আসলে কী নেগোশিয়েট করছে তা পরিমাপ করুন।
ফাইল থাকা সত্ত্বেও 403 Forbidden মানে কী?
একটি স্ট্যাটিক সাইটে 403 সাধারণত HTTP রুল নয়, বরং ফাইলসিস্টেম পারমিশনের সমস্যা। /var/log/nginx/error.log-এ open() ... failed (13: Permission denied) মানে হলো Nginx ওয়ার্কার ইউজার ফাইলটি পড়তে পারছে না। এটি সাধারণত ফাইল মোডের সমস্যার চেয়ে প্যারেন্ট ডিরেক্টরিতে অন্যদের জন্য এক্সিকিউট পারমিশন না থাকার কারণে বেশি ঘটে। directory index of ... is forbidden মানে হলো অনুরোধটি এমন একটি ডিরেক্টরিতে গেছে যেখানে কোনো ইনডেক্স ফাইল নেই এবং autoindex অফ করা আছে। ম্যাচিং location ব্লকে একটি স্পষ্ট deny রুলও 403 রিটার্ন করতে পারে, তাই এরর লগে কিছু না থাকলে সেই ব্লকটি পড়ুন।