এই পর্বে যা শিখবেন
- Forward আর reverse proxy-র পার্থক্য, আর app কেন সরাসরি ইন্টারনেটের সামনে থাকা উচিত নয়
nginx.confপড়া, কোন অনুরোধ কোনserverআরlocation-এ যাবে তা আগে থেকে বলা- Upstream, timeout আর ব্যর্থ backend সামলানো, 502 আর 504 দেখে কারণ খোঁজা
- Let's Encrypt আর ACME দিয়ে TLS, নবায়ন, HSTS
- একটি ছোট app-কে user থেকে health check পর্যন্ত নিজে deploy করা, আর এই পথের সীমা চেনা
১ · Reverse proxy — app-এর সামনের রিসেপশন
পর্ব ৮ থেকে ১৩ পর্যন্ত আপনি Linux, shell, script, systemd, SSH আর network-এর হাতিয়ার শিখেছেন। এই পর্বে সেগুলো এক জায়গায় আসছে। ধরুন একটি কাল্পনিক ছোট বইয়ের দোকানের অ্যাপ আছে, নাম bookstall. অ্যাপটি port 3000-এ চলে। প্রশ্ন হলো, পৃথিবীর মানুষ https://shop.example.com লিখলে সেখানে পৌঁছাবে কীভাবে?
সহজ উত্তর মনে হয়: অ্যাপটিকেই ইন্টারনেটে খুলে দেওয়া হোক। পেশাদার পথ অন্যরকম। অ্যাপের সামনে একটি reverse proxy বসানো হয়।
একটি বড় অফিসের কথা ভাবুন। দর্শনার্থী সোজা বড় কর্মকর্তার ঘরে ঢোকেন না। নিচে রিসেপশন আছে। সেখানে পরিচয় দেখা হয়, কাকে দরকার জিজ্ঞেস করা হয়, ভুল ফ্লোরের মানুষকে ফেরানো হয়, আর খাতায় নাম ওঠে। কর্মকর্তা শুধু আসল কাজ করেন। রিসেপশন হলো reverse proxy, কর্মকর্তা হলো আপনার অ্যাপ। আর forward proxy হলো উল্টো দিকের লোক: অফিসের পিয়ন, যিনি কর্মীদের হয়ে বাইরে কাজ করে আসেন। বাইরের লোক পিয়নকে দেখে, কর্মীকে নয়।
| বিষয় | Forward proxy | Reverse proxy |
|---|---|---|
| কার পক্ষে কাজ করে | Client-এর | Server-এর |
| কে কাকে আড়াল করে | Client-কে বাইরের server থেকে | Server-কে বাইরের client থেকে |
| কে সেটি ঠিক করেন | Client-পক্ষের প্রশাসক | Site-এর মালিক |
| পরিচিত ব্যবহার | অফিসের ইন্টারনেট নিয়ন্ত্রণ, cache | TLS, routing, load balancing |
| উদাহরণ | Squid | Nginx, HAProxy, Caddy |
একটি server-এ Nginx, app আর static ফাইল
Client → Nginx → static files or app
App কেন সরাসরি ইন্টারনেটের সামনে নয়
- TLS এক জায়গায়: Certificate, নবায়ন আর protocol-এর নিয়ম প্রতিটি অ্যাপে আলাদা করে সামলাতে হয় না।
- ধীর client-এর চাপ: দুর্বল মোবাইল নেটওয়ার্কের একজন ব্যবহারকারী ফাইল পাঠাতে অনেকক্ষণ নিতে পারেন। Nginx সেটি জমিয়ে পুরোটা পেলে তবেই অ্যাপকে দেয়, তাই অ্যাপের worker আটকে থাকে না।
- কম অনুমতি: 1024-এর নিচের port সাধারণত বিশেষ অনুমতি চায়। অ্যাপ 3000-এ সাধারণ user হিসেবে চলে, আর port 80, 443 খোলে Nginx-এর master.
- Static ফাইল দ্রুত: CSS বা ছবি দিতে অ্যাপ জাগানোর দরকার হয় না।
- সীমা আর log: Upload-এর আকার, rate limit আর একই ধরনের access log এক জায়গায় থাকে।
- অ্যাপ বদলানোর স্বাধীনতা: পেছনে একাধিক কপি চালানো বা একটি বদলানো সম্ভব, বাইরের ঠিকানা না বদলেই।
Reverse proxy একা নিরাপত্তা নয়। অ্যাপের ভুল, যেমন SQL injection, Nginx-এর ভেতর দিয়ে সোজা চলে যায়। তাছাড়া অ্যাপ যদি 0.0.0.0:3000-এ শোনে আর firewall-এ port 3000 খোলা থাকে, তাহলে Nginx-কে পাশ কাটিয়েই অ্যাপে পৌঁছানো যায়।
২ · Nginx-এর গঠন — master, worker আর event loop
২০০০-এর দশকের শুরুতে web server-এর একটি পরিচিত নকশা ছিল প্রতি সংযোগে একটি process বা thread. ১৯৯৯ সালে Dan Kegel তাঁর লেখায় প্রশ্ন তোলেন, একটি server একসাথে ১০,০০০ client সামলাবে কীভাবে। এটিই পরিচিত হয় C10K সমস্যা নামে। রাশিয়ার প্রকৌশলী Igor Sysoev ২০০২ সালের দিকে Nginx লেখা শুরু করেন, আর ২০০৪ সালে এর প্রথম public release হয়। পরে কোম্পানি গড়ে ওঠে, আর ২০১৯ সালে F5 সেটি কিনে নেয়। C10K-ই Nginx-এর একমাত্র কারণ ছিল, এমন বলা যায় না; তবে সেই সময়ের চাপটি এই নকশার পটভূমি।
দুই রকম ভাতের হোটেল ভাবুন। প্রথমটিতে প্রতি টেবিলে একজন করে ওয়েটার দাঁড়িয়ে থাকেন। রান্নাঘর থেকে খাবার আসতে দেরি হলে তিনিও দাঁড়িয়েই থাকেন। টেবিল বাড়লে ওয়েটারও বাড়াতে হয়। দ্বিতীয়টিতে অল্প কয়েকজন ওয়েটার গোটা হল সামলান। যে টেবিলে কাজ আছে শুধু সেখানে যান, অপেক্ষায় দাঁড়ান না। Nginx-এর event loop এই দ্বিতীয় হোটেল।
Master process
Master config পড়ে, port খোলে, worker চালু করে আর signal শোনে। সে নিজে অনুরোধ সামলায় না। সাধারণত root হিসেবে চলে।
নিচের রিসেপশন ম্যানেজারWorker process
Worker আসল সংযোগ সামলায়। Linux-এ epoll দিয়ে হাজারো সংযোগের মধ্যে যেগুলো তৈরি, শুধু সেগুলো ধরে। চলে কম অনুমতির user, যেমন www-data, হিসেবে।
একটি ছোট হিসাব দেখুন। worker_processes 4 আর worker_connections 1024 হলে সর্বোচ্চ সংযোগ 4 × 1024 = 4096. কিন্তু proxy হিসেবে প্রতিটি client-এর জন্য দুটি সংযোগ লাগে: একটি client-এর সাথে, একটি অ্যাপের সাথে। তাই আনুমানিক 4096 ÷ 2 = 2048 জন একসাথে। এটি ওপরের সীমা, গ্যারান্টি নয়; অ্যাপ আর খোলা ফাইলের সীমা আগেই ফুরিয়ে যেতে পারে।
৩ · nginx.conf পড়া — server আর location
Nginx-এর config হলো কিছু directive আর block-এর গাছ। সবচেয়ে বাইরে events আর http, http-এর ভেতরে এক বা একাধিক server, আর তার ভেতরে location. Debian আর Ubuntu-তে সাইটের ফাইল থাকে sites-available/-এ, আর চালু করতে sites-enabled/-এ symlink দেওয়া হয়। অন্য package-এ প্রায়ই conf.d/ ব্যবহার হয়।
# /etc/nginx/nginx.conf (a short sample, not a full file)
user www-data; # worker runs as this user
worker_processes auto; # usually one per CPU core
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
include /etc/nginx/sites-enabled/*;
}কোন server block অনুরোধ পাবে
একই server-এ একাধিক domain চালানোই virtual host. Nginx আগে IP আর port মেলায়, তারপর অনুরোধের Host header মেলায় server_name-এর সাথে। মেলানোর ক্রম: হুবহু নাম, তারপর সবচেয়ে লম্বা শুরুর wildcard (*.example.com), তারপর সবচেয়ে লম্বা শেষের wildcard, তারপর প্রথম মেলা regex. কিছুই না মিললে যায় সেই port-এর default_server-এ, আর সেটি লেখা না থাকলে ফাইলে প্রথম যে server block আছে সেখানে। তাই অন্য কারও domain আপনার IP-তে এলে সে ভুল করে আপনার সাইট দেখাতে পারে। সেজন্য অনেকে একটি catch-all block রাখেন:
server {
listen 80 default_server;
server_name _; # a name that matches nothing real
return 444; # nginx-only code: close without any reply
}কোন location অনুরোধ পাবে
এই ক্রমটিই ইন্টারভিউর প্রিয় প্রশ্ন, আর বাস্তবের অনেক বাগের উৎস। মনে রাখুন, ফাইলে লেখার ক্রম শুধু regex-এর জন্য গুরুত্বপূর্ণ।
| ধাপ | ধরন | লেখার ধরন | যা ঘটে |
|---|---|---|---|
| ১ | Exact | = /healthz | হুবহু মিললে সাথে সাথে থামে |
| ২ | Prefix | /api/, ^~ /static/ | সবচেয়ে লম্বা মিল মনে রাখে। সেটিতে ^~ থাকলে এখানেই থামে |
| ৩ | Regex | ~ (case মানে), ~* (case মানে না) | ফাইলের ক্রমে যাচাই, প্রথম মিলেই থামে |
| ৪ | ফিরে আসা | — | কোনো regex না মিললে ধাপ ২-এর মনে রাখা prefix |
নিচের ছোট simulator-টি ঠিক এই নিয়ম চালায়। আগে নিজে অনুমান করুন /api/cover.png কোথায় যাবে, তারপর চালান। tests তালিকা বদলে নিজের URI-ও দেখতে পারেন।
// nginx location matching: exact, longest prefix (^~ stops), regex in file order.
// A small model for learning, not nginx's real code.
const locations = [
{ mod: "", path: "/" },
{ mod: "", path: "/api/" },
{ mod: "=", path: "/healthz" },
{ mod: "^~", path: "/static/" },
{ mod: "~*", path: "\\.(png|jpg|css)$" },
{ mod: "~", path: "^/api/v[0-9]+/admin" },
];
const isPrefix = l => l.mod === "" || l.mod === "^~";
const isRegex = l => l.mod === "~" || l.mod === "~*";
function match(uri) {
const exact = locations.find(l => l.mod === "=" && l.path === uri);
if (exact) return [exact, "exact match: stop"];
const best = locations.filter(l => isPrefix(l) && uri.startsWith(l.path))
.sort((a, b) => b.path.length - a.path.length)[0];
if (best && best.mod === "^~") return [best, "longest prefix has ^~: skip regex"];
for (const l of locations.filter(isRegex)) {
const re = new RegExp(l.path, l.mod === "~*" ? "i" : "");
if (re.test(uri)) return [l, "first regex in file order"];
}
return [best, "no regex matched: use longest prefix"];
}
const tests = ["/healthz", "/healthz/", "/static/logo.png", "/img/photo.PNG",
"/api/books", "/api/cover.png", "/api/v2/admin/users", "/about"];
for (const uri of tests) {
const [loc, why] = match(uri);
console.log(uri.padEnd(21), "->", (loc.mod + " " + loc.path).trim());
console.log(" ".repeat(24) + why);
}ফাঁদটি এখানে: /api/cover.png -> ~* \.(png|jpg|css)$. Prefix /api/ মিলেছিল, কিন্তু prefix শুধু মনে রাখা হয়, চূড়ান্ত হয় না। তারপর regex মিলে যাওয়ায় অনুরোধটি API-তে না গিয়ে ছবির নিয়মে চলে গেছে। আর /static/logo.png -> ^~ /static/ দেখায়, ^~ regex-কে সুযোগই দেয় না। /healthz/-এর শেষের একটি / exact মিলকে নষ্ট করে /-এ পাঠিয়েছে।
৪ · Static ফাইল, proxy_pass আর header
Static ফাইল
root /var/www/bookstall; লিখলে /static/app.css খোঁজা হয় /var/www/bookstall/-এ, অর্থাৎ URI-টি root-এর সাথে জোড়া লাগে। alias দিলে location-এর অংশটি বাদ গিয়ে বসে। try_files $uri /index.html; আগে ফাইল খোঁজে, না পেলে index.html দেয়, যা single-page app-এ কাজে লাগে। মনে রাখবেন, worker user-কে ফাইল পড়ার আর প্রতিটি parent ফোল্ডারে ঢোকার অনুমতি পেতে হয় (পর্ব ৮)।
proxy_pass আর শেষের slash
একটি / আচরণ বদলে দেয়। location যদি /api/ হয়:
| লেখা | অনুরোধ | App যা দেখে |
|---|---|---|
proxy_pass http://127.0.0.1:3000; | /api/books | /api/books (URI অপরিবর্তিত) |
proxy_pass http://127.0.0.1:3000/; | /api/books | /books (location অংশ বদলে গেল) |
Header — app-কে সত্যটি জানানো
Proxy বসালে অ্যাপ আর আসল client-কে দেখে না। সংযোগ আসে 127.0.0.1 থেকে, আর TLS শেষ হয়ে যায় Nginx-এই। তাই Nginx-কে তথ্যগুলো header-এ লিখে দিতে হয়।
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}| Header | না দিলে যা হয় |
|---|---|
Host | অ্যাপ domain-এর বদলে proxy_pass-এর নাম বা ঠিকানা দেখতে পারে; ভুল link তৈরি হয় |
| X-Forwarded-For | সব অনুরোধ 127.0.0.1 থেকে এসেছে মনে হয়; log আর rate limit অকেজো |
| X-Forwarded-Proto | অ্যাপ ভাবে অনুরোধ http-তে এসেছে, https-এ পাঠাতে গিয়ে অনন্ত redirect হতে পারে |
TLS কোথায় শেষ হবে
Nginx আর অ্যাপ একই যন্ত্রে থাকলে তাদের মাঝের loopback পথে plain HTTP সাধারণত গ্রহণযোগ্য, কারণ সেই পথ যন্ত্রের বাইরে যায় না। কিন্তু অ্যাপ অন্য server-এ গেলে হিসাব বদলায়। তখন পেছনের পথেও এনক্রিপশন দরকার হতে পারে: proxy_pass https://... আর সাথে proxy_ssl_verify on, নইলে certificate যাচাই ছাড়াই সংযোগ হয়। Load balancer-এ TLS শেষ করার আলোচনা পর্ব ১৮-এ আসবে।
এবার সব জোড়া লাগিয়ে দেখি, একটি অনুরোধ শুরু থেকে শেষ পর্যন্ত কোন পথে যায়। প্রতিটি ধাপের ডেটা খেয়াল করুন।
- DNSনাম → IP
- NginxTLS, server
- LocationURI মেলানো
- UpstreamBackend বাছাই
- উত্তরClient আর log
shop.example.com. 300 IN A
203.0.113.10
SNI : shop.example.com GET /api/books?page=2 HTTP/1.1 Host: shop.example.com listen 443 + server_name মিলল: site block পাওয়া গেল
= /healthz মিলল না / মিলল, দৈর্ঘ্য 1 /api/ মিলল, দৈর্ঘ্য 5, মনে রাখা হলো ~* \.(png|jpg|css)$ মিলল না
GET /api/books?page=2
Host: shop.example.com
X-Real-IP: 198.51.100.4
X-Forwarded-For: 198.51.100.4
X-Forwarded-Proto: https
198.51.100.4 - - [30/Oct/2026:10:00:02 +0600]
"GET /api/books?page=2 HTTP/1.1" 200 812 "-" "curl/8"
- DNS. Browser DNS থেকে নামের IP জানল:
203.0.113.10. উত্তরটি ৩০০ সেকেন্ড TTL পর্যন্ত cache-এ থাকতে পারে। এই ধাপে Nginx কিছুই জানে না। - Nginx-এ পৌঁছানো। TCP সংযোগের পর TLS handshake হলো, Nginx অনুরোধ খুলে পড়ল। এখানেই TLS termination.
listen 443আরserver_nameমিলে site block ঠিক হলো। - Location.
/api/books-এর সবচেয়ে লম্বা prefix/api/. কোনো regex মেলেনি, তাই সেটিই চূড়ান্ত। Query string (?page=2) location মেলানোয় ধরা হয় না। - Upstream. পালা অনুযায়ী
127.0.0.1:3001বাছাই হলো, নতুন header-সহ অনুরোধ গেল। পেছনের এই সংযোগে আর TLS নেই, কারণ এটি একই যন্ত্রের ভেতরে। - উত্তর। App 200 দিল, Nginx তা client-কে ফিরিয়ে দিল আর access log-এ এক লাইন লিখল। ওপরের নমুনায় পাঠানো হয়েছে ৮১২ byte. পর্ব ৯-এর awk দিয়ে এই log-ই পরে বিশ্লেষণ করা যাবে।
৫ · Upstream, timeout আর 502 বনাম 504
অ্যাপের দুটি কপি চালালে একটি বদলানোর সময় অন্যটি কাজ চালিয়ে যেতে পারে। Upstream block-এ backend-গুলোর নাম লেখা হয়, আর Nginx তাদের মধ্যে load balancing করে। ডিফল্ট পদ্ধতি round robin: পালা করে একজনের পর একজন। গভীর তুলনার জন্য দেখুন System Design-এর load balancing পর্ব।
upstream bookstall_app {
# least_conn; # fewest active connections first
server 127.0.0.1:3001 max_fails=2 fail_timeout=5s;
server 127.0.0.1:3002 max_fails=2 fail_timeout=5s;
}
server {
# ...
location /api/ {
proxy_pass http://bookstall_app;
proxy_connect_timeout 3s; # waiting to connect
proxy_read_timeout 30s; # gap between two reads of the reply
proxy_next_upstream error timeout;
}
}| Directive | কীসের সময় | ডিফল্ট |
|---|---|---|
proxy_connect_timeout | Backend-এর সাথে সংযোগ তৈরি | 60s |
proxy_send_timeout | Backend-কে পাঠানোর দুই ধাপের মাঝের ফাঁক | 60s |
proxy_read_timeout | Backend থেকে পড়ার দুই ধাপের মাঝের ফাঁক | 60s |
client_body_timeout | Client-এর পাঠানোর দুই ধাপের মাঝের ফাঁক | 60s |
লক্ষ করুন, proxy_read_timeout পুরো উত্তরের মোট সময় নয়, দুটি read-এর মাঝের সময়। Backend একটু একটু করে পাঠাতে থাকলে অনুরোধটি ৩০ সেকেন্ডের বেশিও চলতে পারে। ডিফল্টগুলো version-ভেদে বদলাতে পারে, তাই ডকুমেন্ট মিলিয়ে নিন। বেশিরভাগ অ্যাপে connect timeout খুব ছোট রাখাই ভালো, কারণ একই যন্ত্রে সংযোগ হতে কয়েক সেকেন্ড লাগার কথা নয়।
max_fails আর fail_timeout হলো passive health check: আসল অনুরোধ ব্যর্থ হলে backend-কে কিছুক্ষণ বাদ রাখা। আলাদা করে নিয়মিত পরীক্ষা পাঠানোর active health check লেখার সময়ে open source Nginx-এর মূল অংশে নেই; সেটি বাণিজ্যিক NGINX Plus-এর সুবিধা হিসেবে পরিচিত, তাই নিজের version-এর ডকুমেন্ট দেখে নিন।
নিচের simulator-এ তিনটি backend: A, B, C. B t=4 থেকে t=11 পর্যন্ত আটকে আছে: সংযোগ নেয়, উত্তর দেয় না। B! মানে B-তে গিয়ে timeout. t=7-এ একটি POST আসে।
// Round-robin upstream: B hangs from t=4 to t=11. One request per simulated second.
// Simplified model of max_fails, fail_timeout and proxy_next_upstream.
const MAX_FAILS = 2, FAIL_TIMEOUT = 5, READ_TIMEOUT_MS = 3000, NORMAL_MS = 20;
const backends = ["A", "B", "C"].map(name => ({ name, fails: 0, checked: 0 }));
const hung = (b, t) => b.name === "B" && t >= 4 && t <= 11;
const usable = (b, t) => b.fails < MAX_FAILS || t - b.checked > FAIL_TIMEOUT;
let next = 0;
function pick(t, tried) {
for (let i = 0; i < backends.length; i++) {
const k = (next + i) % backends.length, b = backends[k];
if (usable(b, t) && !tried.has(b)) { next = (k + 1) % backends.length; return b; }
}
return null;
}
function handle(t, method) {
const tried = new Set(), path = [];
let ms = 0;
for (;;) {
const b = pick(t, tried);
if (!b) return { status: 502, path, ms }; // no live upstream
tried.add(b);
if (!hung(b, t)) { b.fails = 0; path.push(b.name); return { status: 200, path, ms: ms + NORMAL_MS }; }
b.fails++; b.checked = t; ms += READ_TIMEOUT_MS; path.push(b.name + "!");
if (method === "POST") return { status: 504, path, ms }; // already sent: not retried
}
}
const POST_AT = new Set([7]);
console.log(" t method path status ms B.fails");
for (let t = 1; t <= 14; t++) {
const method = POST_AT.has(t) ? "POST" : "GET";
const r = handle(t, method);
console.log(String(t).padStart(2), method.padEnd(6), r.path.join(",").padEnd(7),
String(r.status).padEnd(7), String(r.ms).padEnd(5), backends[1].fails);
}আউটপুটের চারটি সারি মূল গল্প বলে:
5 GET B!,C 200 3020 1: ব্যর্থতা client-এর চোখে পড়েনি, কারণ Nginx C-তে আবার পাঠিয়েছে। কিন্তু তিনি ৩০২০ ms অপেক্ষা করেছেন। Retry error লুকায়, সময় নয়।7 POST B! 504 3000 2: অনুরোধ B-তে পাঠানো হয়ে গেছে, তাই Nginx ডিফল্টে POST আবার পাঠায় না। পাঠালে হয়তো একটি অর্ডার দুবার হতো।10 GET C: B-র fails 2 হওয়ায় সে পালা থেকে বাদ, A আর C ভাগ করে নিচ্ছে।14 GET B 200 20 0:fail_timeoutপেরোনোর পর B আবার সুযোগ পেল, সফল হলো, আর fails শূন্যে ফিরল।
502 বনাম 504
দুটিই বলে সমস্যা Nginx-এর পেছনে। পার্থক্য হলো কীভাবে। আন্দাজ না করে error log পড়ুন।
| Status | অর্থ | Log-এ যা দেখবেন | প্রথম যাচাই |
|---|---|---|---|
| 502 | পেছন থেকে ঠিকঠাক উত্তর পাওয়া যায়নি | connect() failed (111: Connection refused), no live upstreams | অ্যাপ চলছে তো? সঠিক port-এ? systemctl status, ss -tln |
| 504 | সময়ের মধ্যে উত্তর আসেনি | upstream timed out ... while reading response header | ধীর query, আটকে থাকা worker, database |
| 413 | Upload বেশি বড় | client intended to send too large body | client_max_body_size (ডিফল্ট 1 MB) |
| 499 | Client আগেই চলে গেছে (Nginx-এর নিজস্ব কোড) | access log-এ 499 | ব্যবহারকারী অপেক্ষা করতে পারেননি; অ্যাপ ধীর কি? |
504 দেখে proxy_read_timeout বাড়িয়ে ১ ঘণ্টা করা সাধারণ ভুল। এতে অসুখ সারে না, শুধু অপেক্ষা বাড়ে, আর আটকে থাকা সংযোগ জমতে থাকে। লম্বা কাজ অনুরোধের ভেতরে না রেখে queue-তে পাঠানোই ভালো (পর্ব ৭০)।
৬ · TLS — Let's Encrypt, ACME আর নবায়ন
TLS-এর গণিত আর handshake পড়ুন Computer Networks-এর TLS আর HTTPS পর্বে। এখানে আমাদের প্রশ্ন operational: certificate কোথা থেকে আসবে, আর মেয়াদ ফুরানোর দিন রাত ৩টায় site পড়ে যাবে না, এটি কীভাবে নিশ্চিত করবেন?
Let's Encrypt হলো অলাভজনক সংস্থা ISRG-র চালানো একটি CA, যা ২০১৫–১৬ সালের দিকে সবার জন্য খোলে। এটি বিনা খরচে certificate দেয়, মানুষের হাত ছাড়াই, ACME protocol (RFC 8555, ২০১৯) দিয়ে। Client হিসেবে বহুল ব্যবহৃত Certbot. প্রশ্ন একটাই: আপনি যে এই domain-এর মালিক, CA তা জানবে কীভাবে?
ধরুন কেউ বললেন, “ওই বাড়িটি আমার।” পরিদর্শক বললেন, “ঠিক আছে, এই সংকেতটি আপনার বাড়ির গেটে ঝুলিয়ে দিন, আমি রাস্তা থেকে এসে দেখব।” গেটে ঝোলাতে পারলে বাড়ির ওপর আপনার নিয়ন্ত্রণ আছে। HTTP-01 ঠিক এটাই, গেটটি হলো port 80.
| দিন | |
| কে কী করছে | |
| Disk-এ certificate | |
| Nginx দেখাচ্ছে | |
| দেখানো certificate-এর বাকি দিন |
- Order. Certbot তার account key দিয়ে স্বাক্ষর করে CA-কে জানাল,
shop.example.com-এর certificate চাই। - Challenge. CA একটি token দিল। Certbot token আর account key থেকে একটি উত্তর বানিয়ে রাখল, যা পাওয়া যাবে
/.well-known/acme-challenge/-এর নিচে। - যাচাই। CA নিজে
http://shop.example.com/...-এ গিয়ে উত্তরটি পড়ল, port 80 দিয়ে। মিলে গেল। DNS ভুল হলে বা firewall-এ port 80 বন্ধ থাকলে এই ধাপেই ব্যর্থ হয়। - স্বাক্ষর। Certbot CSR পাঠাল, CA সই করে certificate দিল। ফাইল দুটি
fullchain.pemআরprivkey.pem. Nginx reload হলো, বাকি ৯০ দিন। - অপেক্ষা।
certbot.timerদিনে দুবারের মতো চলে আর দেখে নবায়নের সময় হয়েছে কি না। দিন ৫৯-এ বাকি ৩১, তাই কিছু করে না। ৯০ দিনের certificate-এ সাধারণত ৩০ দিন বাকি থাকলে নবায়ন হয়; মেয়াদ ছোট হলে এই হিসাবও বদলায়, তাই নির্দিষ্ট দিনের সংখ্যা ধরে script লিখবেন না। - নবায়ন। দিন ৬০-এ একই challenge আবার হলো, নতুন certificate disk-এ এলো। কিন্তু Nginx memory-তে রাখা পুরোনোটিই দেখাচ্ছে, বাকি ৩০ দিন।
- Reload. Reload-এর পর Nginx নতুনটি পড়ল, বাকি আবার ৯০। এই ধাপটি বাদ পড়লে disk-এ ঠিক certificate থাকা সত্ত্বেও site-এর মেয়াদ ফুরাতে পারে। Certbot-এর nginx plugin বা
--deploy-hookএটি সামলায়।
Wildcard certificate (*.example.com) নিতে হলে DNS-01 লাগে, যেখানে DNS-এ TXT record বসিয়ে প্রমাণ দেওয়া হয়। এটি port 80 ছাড়াও কাজ করে, কিন্তু DNS বদলানোর API key তখন server-এ রাখতে হয়, তাই সেই key-র অনুমতি সীমিত রাখা জরুরি।
server {
listen 443 ssl;
server_name shop.example.com;
ssl_certificate /etc/letsencrypt/live/shop.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/shop.example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
# Start small. Raise max-age only after HTTPS has proved stable.
add_header Strict-Transport-Security "max-age=300" always;
}
server {
listen 80;
server_name shop.example.com;
return 301 https://$host$request_uri;
}HSTS (RFC 6797) browser-কে বলে, এই সময় পর্যন্ত এই site-এ শুধু HTTPS. প্রথম অনুরোধের পর http-তে ফিরে যাওয়ার ঝুঁকি কমে। সাবধান: browser এটি মনে রাখে, তাই বড় max-age দিয়ে পরে HTTPS ভেঙে গেলে ব্যবহারকারীরা ঢুকতেই পারবেন না। সেজন্য ছোট মান দিয়ে শুরু করে ধীরে বাড়ানো নিরাপদ, আর চূড়ান্ত মান প্রায়ই ১ বছর, অর্থাৎ max-age=31536000. আরেকটি ফাঁদ: কোনো location-এ নিজের add_header থাকলে ওপরের স্তরের add_header সেখানে আর উত্তরাধিকারসূত্রে আসে না।
Certificate-এর আয়ু কমছে। ২০২৫ সালে CA/Browser Forum ধাপে ধাপে সর্বোচ্চ মেয়াদ ৪৭ দিনে নামানোর সিদ্ধান্ত নেয়, শেষ ধাপ ২০২৯ সালে নির্ধারিত। Let's Encrypt-ও ঘোষণা করেছে, লেখার সময়ের পরিকল্পনায় ২০২৮ সালের মধ্যে তার ডিফল্ট মেয়াদ ৪৫ দিনে নামবে। তাই সংখ্যা মুখস্থ না করে তাদের বর্তমান ডকুমেন্ট দেখুন, আর ধরে নিন হাতে নবায়ন করার যুগ শেষ।
৭ · নিরাপদে বদলানো — nginx -t, reload আর হালকা করা
Config-এ একটি ; ভুল হলে পুরো site পড়ে যেতে পারে। তাই নিয়মটি অভ্যাস করে নিন: আগে পরীক্ষা, তারপর reload.
$ sudo nginx -t # check the whole config; changes nothing
$ sudo nginx -T # same, and print the full merged config
$ sudo systemctl reload nginx
# নমুনা আউটপুট, version-ভেদে আলাদা হতে পারে
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful| বিষয় | reload | restart |
|---|---|---|
| কী হয় | Master-কে HUP signal, নতুন worker আসে, পুরোনোরা কাজ শেষ করে যায় | পুরো Nginx বন্ধ, তারপর চালু |
| চলমান অনুরোধ | সাধারণত শেষ হতে পারে | কেটে যেতে পারে |
| নতুন config ভুল হলে | পুরোনোটি চলতেই থাকে | বন্ধ হওয়ার পর আর চালু হয় না, site পড়ে যায় |
| কখন | প্রতিদিনের config বদলে | খুব কম ক্ষেত্রে, যেমন package আপগ্রেডের পর নতুন binary চালাতে |
এবার graceful reload-এর ভেতরটা দেখি। সংখ্যাগুলো নমুনা, লক্ষ্য করুন ব্যর্থ অনুরোধের সারিটি।
| Master (PID 900) | |
| পুরোনো worker 901, 902 | |
| পুরোনো worker-এর সংযোগ | |
| নতুন worker 951, 952 | |
| নতুন worker-এর সংযোগ | |
| ব্যর্থ অনুরোধ |
- স্বাভাবিক অবস্থা। দুটি worker ৪০টি সংযোগ সামলাচ্ছে। আপনি একটি নতুন location যোগ করলেন, ফাইল বদলেছে, কিন্তু Nginx এখনো পুরোনো config-এ।
nginx -t. নতুন config পরীক্ষা হলো, ঠিক আছে। চলমান কিছুই বদলায়নি।- Signal.
systemctl reload nginx-এ master HUP পেল আর নতুন config পড়ল। ভুল থাকলে এখানেই থেমে যেত, পুরোনো worker চলতেই থাকত, ব্যবহারকারী কিছুই টের পেতেন না। - নতুন প্রজন্ম। Master নতুন config নিয়ে worker 951 আর 952 চালু করল। একই খোলা port তারা ভাগ করে নেয়, তাই কোনো সংযোগের চেষ্টা ফিরে যায় না।
- বিদায়। পুরোনো worker-রা নতুন সংযোগ নেওয়া বন্ধ করল, কিন্তু হাতের ১২টি সংযোগ শেষ করছে।
- শেষ। পুরোনো সংযোগ ০, worker দুটি বেরিয়ে গেল। পুরো সময়ে ব্যর্থ অনুরোধ ০, আর master-এর PID একই থাকল। লম্বা WebSocket থাকলে পুরোনো worker অনেকক্ষণ থেকে যেতে পারে।
gzip আর cache header, সংক্ষেপে
gzip HTML, CSS, JS আর JSON-এর মতো লেখা ছোট করে। ছবি আর ভিডিও আগেই সংকুচিত, সেগুলোতে শুধু CPU নষ্ট হয়। Cache-Control browser-কে বলে কতদিন জমিয়ে রাখা যাবে। ফাইলের নামে hash থাকলে (app.3f9a.css) লম্বা মেয়াদ নিরাপদ, কারণ বদলালে নামও বদলায়। আরও জানতে দেখুন web caching আর CDN পর্ব।
gzip on;
gzip_types text/css application/javascript application/json;
gzip_min_length 1024;
location ^~ /static/ {
add_header Cache-Control "public, max-age=31536000, immutable";
}বড় config-এ চোখ দিয়ে খোঁজা কষ্টকর। পর্ব ৯-এর awk দিয়ে একটি সারাংশ বানানো যায়। নিচের block একটি নমুনা config-কে শুধু লেখা হিসেবে পড়ে, Nginx চালায় না।
set -euo pipefail
# A sample config kept as text. Nothing here starts nginx.
cat > site.conf <<'EOF'
server {
listen 80;
server_name shop.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name shop.example.com www.shop.example.com;
location /api/ { proxy_pass http://bookstall_app; }
location /img/ { root /var/www/bookstall; }
}
server {
listen 8080;
server_name admin.example.com;
location / { proxy_pass http://127.0.0.1:9000/; }
}
EOF
# One summary line per server block. Assumes "server {" and its "}" start a line.
awk '
/^server \{/ { n++; listen = name = pass = "" }
$1 == "listen" { listen = listen " " $2 }
$1 == "server_name" { for (i = 2; i <= NF; i++) name = name " " $i }
/proxy_pass/ { for (i = 1; i <= NF; i++) if ($i == "proxy_pass") pass = pass " " $(i + 1) }
/^\}/ {
gsub(/;/, "", listen); gsub(/;/, "", name); gsub(/;/, "", pass)
if (pass == "") pass = " (none)"
printf "block %d\n listen :%s\n server_name:%s\n proxy_pass :%s\n", n, listen, name, pass
if (pass != " (none)" && listen !~ /443/) print " REVIEW: proxies traffic without TLS"
}' site.confআউটপুটে তিনটি block. block 1-এ proxy_pass : (none), কারণ সেটি শুধু https-এ redirect করে। block 3-এর নিচে REVIEW: proxies traffic without TLS এসেছে: admin অ্যাপ plain port 8080-এ, যা নজরে আনা দরকার। Regex দিয়ে config পড়া সীমিত উপায়, কারণ include আর একই লাইনের একাধিক block এখানে ধরা হয় না। পুরোপুরি জানতে nginx -T-এর আউটপুট দেখাই ভালো।
৮ · প্রথম deployment — শুরু থেকে শেষ
এবার মডিউলের সব কিছু একসাথে। লক্ষ্য: কাল্পনিক bookstall অ্যাপটি একটি Ubuntu server-এ, shop.example.com-এ, HTTPS দিয়ে। নিচের সব কমান্ড উদাহরণ, এই পাতায় চলে না, আর আউটপুটগুলো নমুনা।
নিজে করে দেখা ঐচ্ছিক। এর জন্য একটি VM বা VPS আর একটি domain লাগবে, যাতে টাকা খরচ হতে পারে। করলে শেষে VM আর তার disk মুছে ফেলুন, আর DNS record সরিয়ে নিন। না করলেও ধাপগুলো পড়ে পুরো পথটি বোঝা যাবে।
ধাপ ১ · DNS আর SSH
Domain-এ A record দিন, যা server-এর IP দেখায়। পর্ব ১২-এর মতো key দিয়ে SSH করুন। পর্ব ১৩-এর dig +short shop.example.com দিয়ে মিলিয়ে নিন। DNS ঠিক না হলে পরে certificate-ও আসবে না।
ধাপ ২ · আলাদা user আর ফাইল
অ্যাপ কখনো root হিসেবে নয়। এটি least privilege: অ্যাপ ভেঙে গেলেও ক্ষতি সীমিত থাকে।
sudo useradd --system --shell /usr/sbin/nologin --home-dir /opt/bookstall bookstall
sudo mkdir -p /opt/bookstall /etc/bookstall /var/www/bookstall/static
# copy the release into /opt/bookstall; it stays owned by root and only readable by the app
sudo install -m 640 -o root -g bookstall /dev/null /etc/bookstall/env
sudoedit /etc/bookstall/env # PORT=3000, HOST=127.0.0.1, secrets; never in gitধাপ ৩ · systemd unit
# /etc/systemd/system/bookstall.service
[Unit]
Description=Bookstall web app
After=network-online.target
Wants=network-online.target
[Service]
User=bookstall
WorkingDirectory=/opt/bookstall
EnvironmentFile=/etc/bookstall/env
ExecStart=/usr/bin/node /opt/bookstall/server.js
Restart=on-failure
RestartSec=3
NoNewPrivileges=true
PrivateTmp=true
[Install]
WantedBy=multi-user.targetsudo systemctl daemon-reload
sudo systemctl enable --now bookstall
systemctl status bookstall --no-pager
curl -i http://127.0.0.1:3000/healthz # test the app before putting Nginx in frontপর্ব ১১-এর মতো, Restart=on-failure অ্যাপ পড়ে গেলে আবার চালু করে। আগে অ্যাপকে সরাসরি পরীক্ষা করে নিলে পরের 502-এর সন্দেহ অর্ধেক কমে।
ধাপ ৪ · Nginx site
server {
listen 80;
server_name shop.example.com;
root /var/www/bookstall;
client_max_body_size 5m;
location ^~ /static/ {
expires 30d;
}
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 3s;
proxy_read_timeout 30s;
}
}sudo ln -s /etc/nginx/sites-available/bookstall /etc/nginx/sites-enabled/bookstall
sudo rm /etc/nginx/sites-enabled/default # only the symlink; the sample stays available
sudo nginx -t && sudo systemctl reload nginxধাপ ৫ · Firewall
sudo ufw allow OpenSSH # FIRST, or you can lock yourself out
sudo ufw allow "Nginx Full" # opens 80 and 443
sudo ufw enable
sudo ufw statusFirewall-এর ক্রম গুরুত্বপূর্ণ: SSH খোলার আগে ufw enable দিলে নিজের সংযোগই হারাতে পারেন। Port 3000 খোলা হয়নি। Cloud-এ এর বাইরে security group-ও মেলাতে হয় (পর্ব ২৭)।
ধাপ ৬ · TLS
sudo certbot --nginx -d shop.example.com # gets the cert and edits the Nginx site
sudo certbot renew --dry-run # rehearses renewal against the staging CA
systemctl list-timers | grep -i certbot # package and snap installs name it differentlyCertbot-এর ইনস্টলের পথ OS-ভেদে আলাদা, তাই তাদের ডকুমেন্টের নির্দেশ অনুসরণ করুন। --dry-run না চালিয়ে কাজ শেষ বলবেন না; ৬০ দিন পরের ব্যর্থতা আজই ধরা পড়ুক।
ধাপ ৭ · Health check আর log
$ curl -i https://shop.example.com/healthz
# নমুনা আউটপুট, version-ভেদে আলাদা হতে পারে
HTTP/1.1 200 OK
Server: nginx
Content-Type: text/plain
okএই যাচাই আপনার ল্যাপটপ থেকে করুন, server থেকে নয়; তাহলে DNS, firewall, TLS, Nginx আর অ্যাপ, পুরো পথটি পরীক্ষা হয়। আর /healthz কী যাচাই করে সেটি ভাবুন: শুধু “process চলছে” নাকি “database-ও পাওয়া যাচ্ছে”।
চালুর পরের প্রথম প্রশ্ন: সব ঠিক আছে তো? উত্তর access log-এ। নিচের block একটি নমুনা log থেকে status code-এর হার বের করে।
set -euo pipefail
cat > access.log <<'EOF'
203.0.113.7 - - [30/Oct/2026:10:00:01 +0600] "GET / HTTP/1.1" 200 5120 "-" "curl/8"
203.0.113.7 - - [30/Oct/2026:10:00:01 +0600] "GET /static/app.css HTTP/1.1" 200 2048 "-" "curl/8"
198.51.100.4 - - [30/Oct/2026:10:00:02 +0600] "GET /api/books HTTP/1.1" 200 812 "-" "curl/8"
198.51.100.4 - - [30/Oct/2026:10:00:03 +0600] "GET /old-page HTTP/1.1" 301 169 "-" "curl/8"
198.51.100.9 - - [30/Oct/2026:10:00:04 +0600] "GET /api/books HTTP/1.1" 200 812 "-" "curl/8"
198.51.100.9 - - [30/Oct/2026:10:00:05 +0600] "GET /favicon.png HTTP/1.1" 404 153 "-" "curl/8"
203.0.113.8 - - [30/Oct/2026:10:00:06 +0600] "POST /api/orders HTTP/1.1" 201 96 "-" "curl/8"
203.0.113.8 - - [30/Oct/2026:10:00:07 +0600] "GET /api/orders/7 HTTP/1.1" 200 240 "-" "curl/8"
203.0.113.9 - - [30/Oct/2026:10:00:08 +0600] "GET /api/report HTTP/1.1" 504 167 "-" "curl/8"
203.0.113.9 - - [30/Oct/2026:10:00:09 +0600] "GET /api/books HTTP/1.1" 502 157 "-" "curl/8"
203.0.113.9 - - [30/Oct/2026:10:00:10 +0600] "GET /api/report HTTP/1.1" 504 167 "-" "curl/8"
198.51.100.2 - - [30/Oct/2026:10:00:11 +0600] "GET / HTTP/1.1" 200 5120 "-" "curl/8"
198.51.100.2 - - [30/Oct/2026:10:00:12 +0600] "GET /static/app.css HTTP/1.1" 304 0 "-" "curl/8"
198.51.100.6 - - [30/Oct/2026:10:00:13 +0600] "GET /api/books HTTP/1.1" 200 812 "-" "curl/8"
198.51.100.6 - - [30/Oct/2026:10:00:14 +0600] "GET /api/report HTTP/1.1" 200 3300 "-" "curl/8"
203.0.113.7 - - [30/Oct/2026:10:00:15 +0600] "GET /api/books HTTP/1.1" 200 812 "-" "curl/8"
EOF
# In the combined log format, field 9 is the status code and field 7 the path.
echo "== status codes"
awk '{ c[$9]++ } END { for (s in c) printf "%s %d %.2f%%\n", s, c[s], c[s] * 100 / NR }' access.log | sort
echo "== by class"
awk '{ c[substr($9, 1, 1) "xx"]++ } END { for (k in c) printf "%s %d\n", k, c[k] }' access.log | sort
echo "== 5xx rate"
awk '$9 >= 500 { e++ } END { printf "%d of %d = %.2f%%\n", e, NR, e * 100 / NR }' access.log
echo "== paths behind the 5xx"
awk '$9 >= 500 { print $9, $7 }' access.log | sort | uniq -c | sort -rnCombined format-এ $9 হলো status, $7 হলো path. মোট ১৬টি লাইনের ৯টি 200, অর্থাৎ 200 9 56.25%. শ্রেণি অনুযায়ী 5xx 3, আর 3 of 16 = 18.75%, যা ছোট নমুনা হলেও স্পষ্ট বিপদসংকেত। শেষ অংশ কারণের দিকে ইঙ্গিত করে: 2 504 /api/report, অর্থাৎ একটি ধীর path, আর 1 502 /api/books, অর্থাৎ এক মুহূর্তের জন্য অ্যাপে সংযোগ হয়নি। মনে রাখবেন, 304 কোনো সমস্যা নয়; এটি বলে browser-এর জমানো কপিই চলবে।
পরের version ছাড়বেন কীভাবে
প্রথমবারের পরের কাজই আসল কাজ। একটি পরিচিত হাতে-করা পথ হলো প্রতিটি version আলাদা ফোল্ডারে রাখা, যেমন /opt/bookstall/releases/42, আর current নামে একটি symlink সেদিকে দেখানো। নতুন version আনলে symlink বদলে service restart করুন। সমস্যা হলে symlink আগের ফোল্ডারে ফিরিয়ে আবার restart করলেই rollback. তবে মনে রাখবেন, এতে restart-এর কয়েক সেকেন্ড অ্যাপ বন্ধ থাকে, আর database-এর বদল এভাবে ফেরানো যায় না।
৯ · কী ভাঙে, আর হাতে করা পথের সীমা
| লক্ষণ | সম্ভাব্য কারণ | যেখানে দেখবেন |
|---|---|---|
| সব পাতায় 502 | অ্যাপ বন্ধ, ভুল port | journalctl -u bookstall, ss -tln |
502, log-এ (13: Permission denied) | RHEL-ধরনের OS-এ SELinux proxy সংযোগ আটকাচ্ছে | SELinux-এর log আর boolean |
| Static ফাইলে 403 | worker user parent ফোল্ডারে ঢুকতে পারছে না | namei -l <path> |
| Certificate নবায়ন ব্যর্থ | Port 80 বন্ধ, DNS বদলেছে, hook নেই | certbot renew --dry-run |
| অনন্ত redirect | X-Forwarded-Proto নেই | curl -I -L, অ্যাপের proxy সেটিং |
| Disk ভরে যাওয়া | Log rotation নেই | du -sh /var/log/nginx, logrotate |
| অজানা domain-এ আপনার site | Catch-all default_server নেই | curl -H "Host: x.test" |
এই পথ কোথায় থামে
ছোট প্রজেক্টে একটি server-এ এই সাজানো সম্পূর্ণ যুক্তিসঙ্গত, আর অনেক দলের জন্য দীর্ঘদিন যথেষ্ট। একটি হিসাব করে দেখা যাক। ধরে নিই একটি server-এর availability ৯৯.৫% (অনুমিত সংখ্যা, provider-এর দাবি নয়)। ৩০ দিনে মোট ৭২০ ঘণ্টা, তাই আনুমানিক downtime ৭২০ × ০.০০৫ = ৩.৬ ঘণ্টা। একটি অভ্যন্তরীণ টুল বা ছোট দোকানের জন্য এটি হয়তো মেনে নেওয়া যায়। Payment সেবার জন্য নয়। আসল প্রশ্ন হলো, ওই ৩.৬ ঘণ্টার দাম আপনার কাছে কত, আর তা কমাতে জটিলতার দাম কত।
সীমাগুলো জেনে রাখা দরকার। বাকি কোর্স আসলে এই তালিকারই উত্তর।
| সমস্যা | কেন | কোর্সের যেখানে উত্তর |
|---|---|---|
| একটি server | পুরো ব্যবস্থাই single point of failure | পর্ব ১৭–১৮: autoscaling, cloud load balancer |
| হাতে বানানো | আবার হুবহু বানানো কঠিন, হয়ে যায় snowflake server | পর্ব ১৬: machine image; পর্ব ৫১–৫২: IaC আর Terraform; পর্ব ৫৬: Ansible |
| “আমার মেশিনে চলে” | Runtime-এর version server-এ আলাদা | পর্ব ৩৬–৪০: container আর Docker |
| Deploy হাতে | ভুলের ঝুঁকি, rollback-এর পথ নেই | পর্ব ৫৭–৫৯: pipeline আর deployment strategy |
| Secret ফাইলে | ঘোরানো আর নিরীক্ষা কঠিন | পর্ব ৩৩: secret আর KMS |
| দেখার চোখ নেই | ব্যবহারকারী জানানোর পর আপনি জানেন | পর্ব ৬৩–৬৬: metric, log আর alert |
| অনেক service | হাতে config সামলানো যায় না | পর্ব ৪২–৪৬: Kubernetes আর Ingress |
Nginx-এর বিকল্প
| Tool | কোথায় শক্তিশালী | কখন ভাববেন |
|---|---|---|
| Caddy | ডিফল্টেই স্বয়ংক্রিয় HTTPS, ছোট config | ছোট site, কম ঝামেলা চাই |
| HAProxy | L4 আর L7 load balancing, বিস্তারিত health check | মূল কাজ load balancing |
| Traefik | Docker ও Kubernetes থেকে নিজে route খুঁজে নেয় | Container-ভিত্তিক পরিবেশ |
| Apache HTTP Server | পুরোনো, বহু module, .htaccess | Shared hosting বা পুরোনো ব্যবস্থা |
Cloud-এ অনেক সময় Nginx-এর একটি অংশের কাজ managed load balancer নেয় (পর্ব ১৮), তবে ধারণাগুলো একই থাকে: listener, routing rule, target, health check আর timeout.
আরও গভীরে: Reload-এর পেছনে Unix-এর পুরোনো কৌশল আছে: master socket খোলে, আর fork-এ worker সেই descriptor পায়। একই কৌশলে Nginx চলতে চলতে নিজের binary-ও বদলাতে পারে, USR2 signal দিয়ে। Event loop-এর দুর্বলতা হলো একটি blocking কাজ পুরো worker থামিয়ে দেয়; সেজন্য নতুন version-এ disk I/O-র জন্য thread pool যোগ হয়েছে। Client IP-র জন্য প্রমিত Forwarded header আছে (RFC 7239, ২০১৪), তবে বাস্তবে X-Forwarded-For-ই বেশি দেখা যায়।
পরীক্ষা ও ইন্টারভিউতে:
- Forward আর reverse proxy-র পার্থক্য? প্রথমটি client-এর পক্ষে, দ্বিতীয়টি server-এর পক্ষে কাজ করে।
- Location-এর ক্রম? Exact, সবচেয়ে লম্বা prefix (
^~হলে থামা), regex ফাইলের ক্রমে, শেষে সেই prefix. - 502 আর 504-এর পার্থক্য? ঠিকঠাক উত্তর পায়নি বনাম সময়ের মধ্যে উত্তর পায়নি।
- Reload আর restart? Reload চলমান সংযোগ রেখে worker বদলায়; restart পুরো process বন্ধ করে।
- HTTP-01 কীভাবে কাজ করে? CA port 80 দিয়ে নির্দিষ্ট URL থেকে token-ভিত্তিক উত্তর পড়ে।
১০ · AI-এর যুগে Nginx আর deployment
AI কয়েক সেকেন্ডে Nginx config বা পুরো deployment script লিখে দেয়। দেখতে ঠিকঠাকও লাগে। সমস্যা হলো config-এর ভুল প্রায়ই error দেয় না, শুধু চুপচাপ ভুল কাজ করে।
| AI-এর ভুল | ফল | যাচাই |
|---|---|---|
অ্যাপ 0.0.0.0-এ, firewall-এ 0.0.0.0/0 থেকে 3000 খোলা | Nginx-কে পাশ কাটানো যায় | বাইরে থেকে port পরীক্ষা, ss -tln |
অ্যাপ root হিসেবে চালানো, chmod -R 777 | ছোট ভুল পুরো server-এর ঝুঁকি | Unit-এর User=, ফাইলের অনুমতি |
| Secret সরাসরি unit বা config-এ, public repo-তে | Secret ফাঁস | আলাদা ফাইল, secret scanning |
| নেই এমন directive বা ভুল নাম | nginx -t ব্যর্থ | Nginx-এর অফিসিয়াল directive তালিকা |
পুরোনো listen 443 ssl http2;, ssl on; | Warning বা error, version-ভেদে | নিজের version-এর ডকুমেন্ট |
| Location-এর ক্রম না বুঝে regex যোগ | API-র অনুরোধ অন্য নিয়মে যায় | নমুনা URI দিয়ে curl |
| 504-এর “সমাধান”: timeout ৩৬০০ সেকেন্ড | আসল ধীরতা লুকিয়ে থাকে | কোন path ধীর, তা log থেকে মাপা |
সতর্কতা ছাড়া restart, rm -rf | Downtime বা ডেটা হারানো | আগে nginx -t, কমান্ডের অর্থ পড়া |
AI-কে config চাওয়ার সময় পরিবেশ, সীমা আর যাচাই একসাথে চান। আর real domain বা secret নয়, উদাহরণ মান দিন।
AI-এর দেওয়া config চালানোর আগে — চেকলিস্ট
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন — তারপর “→ উত্তর” চাপুন।
প্র ০১Event loop কি প্রতি সংযোগে thread-এর চেয়ে সবসময় ভালো?
না। প্রশ্নটি কাজের ধরনের ওপর নির্ভর করে। C10K আলোচনার মূল কথা ছিল, বেশিরভাগ সংযোগ বেশিরভাগ সময় কিছু করে না; তারা নেটওয়ার্কের অপেক্ষায় থাকে। প্রতি সংযোগে একটি thread থাকলে সেই অলস অপেক্ষার জন্যও stack-এর memory আর scheduler-এর খরচ দিতে হয়। ১৯৯০-এর দশকে এটি মেনে নেওয়া যেত, কিন্তু ইন্টারনেট বড় হওয়ায় আর ধীর ক্লায়েন্টের সংযোগ দীর্ঘ হওয়ায় হিসাব বদলায়। Dan Kegel-এর ১৯৯৯ সালের লেখা এই প্রশ্নকেই সামনে আনে। Linux-এর epoll, FreeBSD-র kqueue এই ধরনের নকশাকে কার্যকর করে।
তবে event loop-এর একটি কঠিন শর্ত আছে: কোনো কাজ block করা চলবে না। Nginx-এর worker যদি ধীর disk-এ আটকে যায়, তার হাতের হাজার সংযোগও আটকে যায়। সেজন্যই পরে thread pool যোগ হয়েছে। Node.js-এ এই ব্যথা আরও পরিচিত: একটি ভারী হিসাব main thread ধরে রাখলে পুরো server সাড়া দেয় না।
উল্টো দিকে, Go-র goroutine বা Java-র virtual thread দেখায় যে “প্রতি সংযোগে একটি হালকা thread” লেখা সহজ রেখেও অনেক সংযোগ সামলানো যায়; ভেতরে runtime সেই event-ভিত্তিক কাজই করে। আর CPU-ভারী কাজে event loop কোনো বাড়তি সুবিধা দেয় না, সেখানে core-ই আসল সীমা। Nginx-এর ক্ষেত্রে event loop মানায়, কারণ তার কাজ মূলত byte পাঠানো আর অপেক্ষা করা। Apache-ও পরে event-ভিত্তিক MPM যোগ করেছে, তাই আজকের তুলনা আগের মতো সাদা-কালো নয়।
মূল উপলব্ধি: নকশা বাছাই হয় কাজের আকার দেখে। অপেক্ষা-প্রধান কাজে event loop চমৎকার, কিন্তু তার দাম হলো “কখনো block নয়” শৃঙ্খলা।
প্র ০২HTTP-01 আসলে কী প্রমাণ করে, আর certificate কী প্রমাণ করে না?
HTTP-01 প্রমাণ করে, এই মুহূর্তে যিনি অনুরোধ করেছেন, এই নামে আসা HTTP অনুরোধের উত্তর তিনি নিয়ন্ত্রণ করতে পারেন। একে বলে domain validation. এটি প্রমাণ করে না যে সাইটটি সৎ, কোম্পানিটি আসল, বা তথ্যগুলো সত্য। Phishing সাইটও বৈধ certificate পেতে পারে। তাই browser-এর তালা মানে “সংযোগ এনক্রিপ্টেড, আর এই নামের সাথেই কথা হচ্ছে”, “সাইটটি নিরাপদ” নয়। প্রতিষ্ঠান যাচাই করা OV আর EV certificate আছে, তবে browser আজ সেই পার্থক্য খুব একটা সামনে দেখায় না।
Let's Encrypt আসার আগে certificate সাধারণত কিনতে হতো, হাতে CSR বানিয়ে পাঠাতে হতো, আর বছরে একবার নবায়ন ভুলে গেলে site পড়ে যেত। ফলে ছোট সাইটগুলো প্রায়ই http-তেই থেকে যেত। ACME এই পুরো কাজটিকে যন্ত্রের হাতে দেয়। ৯০ দিনের ছোট মেয়াদের দুটি যুক্তি Let's Encrypt জানিয়েছে: key ফাঁস হলে ক্ষতির সময় সীমিত থাকে, আর automation করতে উৎসাহ দেওয়া হয়। Revocation বাস্তবে সবসময় নির্ভরযোগ্য নয়, তাই ছোট মেয়াদ একরকম নিরাপত্তা জাল। ২০২৫ সালে CA/Browser Forum-এর সিদ্ধান্তে সর্বোচ্চ মেয়াদ ধাপে ধাপে ৪৭ দিনে নামার পথে আছে।
আপনার করণীয় প্রতিরক্ষামূলক: DNS account-এ MFA রাখা, কারণ DNS যাঁর হাতে, certificate-ও কার্যত তাঁর হাতে। CAA record দিয়ে কোন CA আপনার domain-এ certificate দিতে পারবে তা সীমিত করা। আর Certificate Transparency log নজরে রাখা, যাতে আপনার domain-এ অজানা certificate দেখলে টের পান।
মূল উপলব্ধি: Certificate পরিচয়ের একটি সীমিত দাবি। Automation-এর আসল উপহার শুধু সুবিধা নয়, ছোট মেয়াদের মতো নিরাপদ নকশাকে সম্ভব করা।
প্র ০৩X-Forwarded-For-কে অন্ধভাবে বিশ্বাস করলে কী হয়?
এই header শুধু একটি লেখা। Client নিজেই যা খুশি লিখে পাঠাতে পারে। Nginx-এর $proxy_add_x_forwarded_for আগের মানের শেষে আসল সংযোগের IP জুড়ে দেয়। ফলে তালিকার বাঁ দিকের অংশ client-এর হাতে লেখা হতে পারে, আর ডান দিকের অংশ আপনার নিজের proxy-র লেখা।
অ্যাপ যদি তালিকার প্রথম IP-কে আসল ধরে, তাহলে কয়েকটি ক্ষতি হয়। Rate limit প্রতি IP-তে হলে তা অকার্যকর হয়ে যায়। Admin অংশ “শুধু অফিসের IP থেকে” নিয়মে বাঁধা থাকলে সেই নিয়ম দুর্বল হয়। Audit log-এ ভুল ঠিকানা ওঠে, ফলে পরে তদন্ত বিভ্রান্ত হয়। অর্থাৎ যে তথ্য নিরাপত্তার সিদ্ধান্তে লাগে, সেটি যাচাই ছাড়া নেওয়া হচ্ছে।
সঠিক ধারণাটি হলো trust boundary. আপনি জানেন আপনার সামনে কতগুলো নিজের proxy আছে। ডান দিক থেকে সেই proxy-গুলো বাদ দিয়ে যে প্রথম ঠিকানা আসে, সেটিই বিশ্বাসযোগ্য client IP. Nginx-এর real_ip module-এ set_real_ip_from দিয়ে বিশ্বস্ত proxy-র তালিকা দেওয়া যায়, আর Express বা Django-র মতো framework-এও “trusted proxy” সেটিং থাকে। Load balancer বা CDN সামনে থাকলে এই গোনা বদলে যায়, তাই পর্ব ১৮-এ যাওয়ার পর আবার যাচাই করতে হবে। ২০১৪ সালে RFC 7239 Forwarded header প্রমিত করে, তবে বিশ্বাসের সমস্যা তাতে নিজে থেকে মেটে না। একই সতর্কতা X-Forwarded-Proto আর X-Forwarded-Host-এও খাটে, তাই সবচেয়ে বাইরের proxy-তে এগুলো client-এর মান মেনে না নিয়ে নিজে বসানোই ভালো।
মূল উপলব্ধি: Network-এর ভেতরের যেকোনো তথ্যের আগে প্রশ্ন করুন, “এটি কে লিখেছে, আর আমি কি তাকে বিশ্বাস করি?” Header কোনো প্রমাণ নয়, দাবি।
প্র ০৪Backend ব্যর্থ হলে proxy নিজে থেকে আবার পাঠাবে: এটি কি সবসময় ভালো?
অনেক সময় ভালো, সবসময় নয়। GET-এর মতো অনুরোধ আবার পাঠালে সাধারণত ক্ষতি নেই, তাই একটি backend পড়ে গেলেও ব্যবহারকারী টের পান না। সমস্যা হয় POST-এ। ধরুন /api/orders backend-এ পৌঁছেছে, অর্ডার database-এ লেখা হয়েছে, কিন্তু উত্তর ফেরার আগেই সংযোগ কেটে গেল। Proxy-র কাছে এটি ব্যর্থতা, কিন্তু আসলে কাজ হয়ে গেছে। আবার পাঠালে একই অর্ডার দুবার হবে, হয়তো টাকাও দুবার কাটবে।
এই কারণেই Nginx ১.৯.১৩ থেকে, অনুরোধ একবার backend-এ পাঠানো হয়ে গেলে, POST-এর মতো non-idempotent method ডিফল্টে পরের server-এ পাঠায় না। চাইলে non_idempotent দিয়ে তা চালু করা যায়, তবে তখন দায় আপনার। লক্ষ করুন “পাঠানো হয়ে গেলে” শব্দ দুটি: connection refused হলে অনুরোধ যায়ইনি, তাই POST-ও আরেক backend-এ যেতে পারে।
দ্বিতীয় বিপদ হলো retry-এর ঢেউ, যাকে অনেক সময় retry storm বলা হয়। সব backend ধীর হলে retry প্রতিটি অনুরোধকে দ্বিগুণ করে, আর ব্যবস্থা আরও ডুবে যায়। Client, proxy আর অ্যাপ, তিন স্তরে retry থাকলে সংখ্যা গুণ হয়: ৩ × ৩ × ৩ = ২৭ চেষ্টা। আর simulator-এ দেখেছেন, retry-তে client ৩ সেকেন্ড বাড়তি অপেক্ষা করে। সেজন্যই proxy_next_upstream_tries আর proxy_next_upstream_timeout দিয়ে সীমা দেওয়া যায়।
নিরাপদ পথ হলো অ্যাপকে idempotency শেখানো: client একটি অনন্য key পাঠায়, আর অ্যাপ একই key দ্বিতীয়বার দেখলে নতুন অর্ডার না বানিয়ে আগের ফল ফেরত দেয় (পর্ব ৬-এ দেখেছেন)।
মূল উপলব্ধি: Retry শুধু তখনই নিরাপদ, যখন পুনরাবৃত্তিতে ক্ষতি নেই আর তার সংখ্যা সীমিত। “ব্যর্থ” মানে সবসময় “হয়নি” নয়।
প্র ০৫Nginx reload-এ সংযোগ কাটে না। তাহলে কি আপনার deployment-ও শূন্য downtime-এর?
না, আর এই ভুল ধারণা বেশ সাধারণ। Reload শুধু Nginx-এর config বদলায়। ওপরের deployment-এ অ্যাপ নতুন version-এ নিতে আপনি systemctl restart bookstall চালাবেন। তখন পুরোনো process বন্ধ হয়ে নতুনটি প্রস্তুত হওয়া পর্যন্ত পেছনে কেউ থাকে না। সেই কয়েক সেকেন্ডের সব অনুরোধে 502 আসে। একটি মাত্র backend থাকলে Nginx-এর কিছু করার নেই।
সমাধানের জন্য অন্তত দুটি কপি লাগে। পুরোনো সংস্করণ 3001-এ চলছে। নতুনটি 3002-এ চালু করুন, তার /healthz সফল হওয়া পর্যন্ত অপেক্ষা করুন, upstream-এ ঠিকানা বদলে nginx -t আর reload করুন, চলমান অনুরোধ শেষ হলে পুরোনোটি বন্ধ করুন। এটিই হাতে করা blue-green deployment. পর্ব ৫৯-এ এর পরিণত রূপ দেখবেন। Health check-এর ধাপটি বাদ দিলে সুবিধা অর্ধেক; নতুন process চালু হওয়া মানেই সে অনুরোধ নেওয়ার জন্য প্রস্তুত নয়।
এর ভেতরেও ফাঁদ আছে। প্রথমত, app-কে SIGTERM পেয়ে হাতের কাজ শেষ করে বেরোতে হবে; নইলে চলমান অনুরোধ কাটে। দ্বিতীয়ত, database-এর schema বদলালে পুরোনো আর নতুন version কিছুক্ষণ একসাথে চলে, তাই বদলটি দুই version-এর সাথেই খাপ খেতে হবে। তৃতীয়ত, WebSocket-এর মতো লম্বা সংযোগ থাকলে পুরোনো worker অনেকক্ষণ টিকে থাকে, memory দ্বিগুণের কাছাকাছি হতে পারে; worker_shutdown_timeout এর সীমা দেয়, তবে সময় ফুরালে সেই সংযোগ কাটে। চতুর্থত, reload বারবার হলে, যেমন খুব ঘন ঘন config তৈরি হলে, কয়েক প্রজন্মের worker একসাথে থাকতে পারে।
মূল উপলব্ধি: “শূন্য downtime” একটি স্তরের গুণ নয়, পুরো পথের গুণ। Proxy, app, database আর client, প্রতিটিকে একসাথে দুই অবস্থা সহ্য করতে হয়।
অনুশীলন
প্রথমে নিজে চেষ্টা করুন, তারপর উত্তর মেলান। কোনোটির জন্য server ভাড়া লাগবে না।
-
হার বের করুন: ৫ মিনিটের একটি access log-এ মোট 12,000টি অনুরোধ, এর মধ্যে 180টির status 5xx. (ক) 5xx হার কত? (খ) লক্ষ্য যদি ৯৯.৯% সফলতা হয়, তাহলে অনুমোদিত ব্যর্থতার হারের কত গুণ এটি? (গ) এই সংখ্যা বের করার awk কমান্ড লিখুন।
(ক) 180 ÷ 12,000 = 0.015, অর্থাৎ ১.৫%. (খ) অনুমোদিত হার ১০০ − ৯৯.৯ = ০.১%, তাই ১.৫ ÷ ০.১ = ১৫ গুণ। পর্ব ৬৬-এ এই ধারণাই burn rate নামে আসবে। (গ) নিচের কমান্ডটি 5xx আর মোট, দুটিই গোনে:
awk '$9 >= 500 { e++ } END { printf "%.2f%%\n", e * 100 / NR }' access.log -
ফল আগে বলুন: একটি চলমান site-এ এই ক্রমে কাজ হলো: (১) config-এ একটি
;বাদ পড়ল (২)systemctl reload nginx(৩) কিছুক্ষণ পরেsystemctl restart nginx. প্রতিটি ধাপের পর site-এর অবস্থা কী?ধাপ ১-এর পর: কিছুই বদলায়নি, Nginx memory-তে পুরোনো config-এ চলছে। ধাপ ২-এর পর: নতুন config পড়া ব্যর্থ, তাই পুরোনো worker চলতেই থাকে, site ঠিক আছে। Package-ভেদে কমান্ড error দেখাতে পারে, আবার চুপ থেকে শুধু
error.log-এ[emerg]লিখতে পারে। ধাপ ৩-এর পর: পুরোনো process বন্ধ হলো, নতুনটি চালু হতে পারল না, site পড়ে গেল। শিক্ষা: reload-এর “চুপ” সাফল্য নয়, আগেnginx -tচালান। -
Config সমালোচনা করুন: নিচের সাজানোয় অন্তত চারটি সমস্যা খুঁজুন।
# app: node server.js as root, HOST=0.0.0.0, PORT=3000 # firewall: 22, 80, 443 and 3000 open to 0.0.0.0/0 server { listen 80; server_name shop.example.com; location / { proxy_pass http://127.0.0.1:3000; proxy_read_timeout 3600s; } }- অ্যাপ root হিসেবে: আলাদা
nologinuser আর systemd unit দরকার। - Port 3000 খোলা,
0.0.0.0-এ শোনা: Nginx-কে পাশ কাটানো যায়।127.0.0.1-এ বাঁধুন, port বন্ধ করুন। - TLS নেই: Certificate আর redirect যোগ করুন।
- Header নেই:
Host,X-Forwarded-For,X-Forwarded-Protoছাড়া অ্যাপ ভুল তথ্য পায়। - Timeout ১ ঘণ্টা: ধীর path লুকিয়ে থাকে, আটকে থাকা সংযোগ জমে।
- SSH সবার জন্য খোলা: সম্ভব হলে নিজের IP বা bastion-এ সীমিত করুন (পর্ব ১২)।
- অ্যাপ root হিসেবে: আলাদা
-
Log দেখে রোগ ধরুন: নিচের তিনটি
error.log-এর লাইনের জন্য বলুন ব্যবহারকারী কোন status দেখেছেন, আর আপনি প্রথমে কী যাচাই করবেন। (ক)connect() failed (111: Connection refused) while connecting to upstream(খ)upstream timed out (110: Connection timed out) while reading response header from upstream(গ)client intended to send too large body(ক) 502: ঠিকানায় কেউ শুনছে না।
systemctl status bookstall,journalctlআরss -tlnদেখুন; অ্যাপ পড়ে গেছে, নাকি port মেলেনি। (খ) 504: সংযোগ হয়েছে, কিন্তু উত্তর আসেনি। কোন path ধীর, database আটকে আছে কি না দেখুন। (গ) 413: Upload বড়। সীমা বাড়ানো সত্যিই দরকার কি না ভেবেclient_max_body_sizeবদলান।
নিজেকে যাচাই করুন
ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।
ক্রমএকটি ছোট অ্যাপের প্রথম deployment-এর ধাপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)
- DNS-এ domain-এর A record বসানো
- আলাদা user তৈরি আর অ্যাপ systemd service হিসেবে চালু
- Nginx site লেখা,
nginx -t, তারপর reload - Firewall-এ SSH, 80 আর 443 খোলা
- Certbot দিয়ে certificate নেওয়া আর নবায়নের dry-run
- বাইরে থেকে HTTPS health check
Nginx config-এ location /api/ { ... } আর location ~* \.png$ { ... } আছে। /api/cover.png অনুরোধ কোনটিতে যাবে?
- /api/, কারণ prefix আগে মিলেছে
- ~* \.png$, কারণ মেলা regex prefix-কে হারিয়ে দেয়
- কোনোটিতেই নয়, 404 আসবে
- দুটিতেই পরপর যাবে
ব্যবহারকারী 502 দেখছেন, আর error.log-এ আছে connect() failed (111: Connection refused) while connecting to upstream. সবচেয়ে সম্ভাব্য কারণ কোনটি?
- Database query ধীর
- TLS certificate-এর মেয়াদ শেষ
- অ্যাপ বন্ধ, অথবা ভুল port-এ শুনছে
- Upload-এর আকার সীমার বেশি
location /api/ { proxy_pass http://127.0.0.1:3000/; } থাকলে /api/books অনুরোধে অ্যাপ কোন path দেখবে?
- /books
- /api/books
- /
- /api/api/books
ACME-র HTTP-01 challenge-এ CA কীভাবে domain-এর নিয়ন্ত্রণ যাচাই করে?
- মালিকের ইমেইলে একটি link পাঠিয়ে
- DNS-এ একটি TXT record পড়ে
- Server-এর private key হাতে নিয়ে
- Port 80-এ নির্দিষ্ট URL থেকে token-ভিত্তিক উত্তর পড়ে
চলমান Nginx-এর config-এ syntax ভুল থাকা অবস্থায় reload করলে সাধারণত কী হয়?
- Nginx সাথে সাথে বন্ধ হয়ে যায়
- পুরোনো config-এ Nginx চলতেই থাকে
- ভুল লাইন বাদ দিয়ে বাকি অংশ চালু হয়
- পুরো server reboot হয়
Nginx-এর পেছনে থাকা অ্যাপে প্রতি IP-র rate limit বসাতে হবে। Client IP নেওয়ার সবচেয়ে ভালো উপায় কোনটি?
- X-Forwarded-For-এর সবচেয়ে বাঁ দিকের IP সরাসরি নেওয়া
- অ্যাপের দেখা সংযোগের IP নেওয়া
- বিশ্বস্ত proxy-র তালিকা ঠিক করে ডান দিক থেকে তাদের বাদ দেওয়া
- User-Agent header থেকে IP পড়া
শব্দকোষ
লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন — অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:
মনে রাখুন
- Reverse proxy server-এর পক্ষে দাঁড়ায়; অ্যাপ থাকে
127.0.0.1-এ, কম অনুমতিতে। - Location: exact → লম্বা prefix → regex → সেই prefix. শুধু regex-এ ফাইলের ক্রম গুরুত্বপূর্ণ।
- 502 মানে ঠিকঠাক উত্তর পাওয়া যায়নি, 504 মানে সময়মতো আসেনি। আগে
error.log. - Certificate স্বয়ংক্রিয় হোক: ACME, timer, reload hook আর
--dry-run. - আগে
nginx -t, তারপর reload. আর মনে রাখুন, এই হাতে করা পথের সীমাই বাকি কোর্সের সূচি।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পর্ব ১৫ · Cloud VM — instance type, vCPU, boot আর lifecycleপরবর্তী পর্বযে server-এ আজ deploy করলেন, cloud-এ তা কীভাবে পাবেন।
- ক্লাউড কম্পিউটিং ও DevOps · কোর্স হোম৮১টি পর্বপুরো কোর্সের রোডম্যাপ।
- Computer Networks · TLS আর HTTPSগভীরে যানHandshake, certificate chain আর Web PKI.
- System Design · Load balancingগভীরে যানL4 আর L7, বিভিন্ন algorithm আর health check.
- Computer Networks · Web caching আর CDNপাশের ট্র্যাকCache header আর CDN-এর ভেতরের কাজ।
- System Design · Deployment আর releaseপাশের ট্র্যাকনিরাপদে নতুন version ছাড়ার নকশা।