Reverse Proxy و TLS: اللي واقف قدام الـ app

جارٍ التحميل…

01 / ملخص الدرس

Reverse Proxy و TLS: اللي واقف قدام الـ app

الـ rate limit بيقفل كل الـ users مع بعض لأنهم كلهم ليهم IP الـ proxy، وأي حد يكتب X-Forwarded-For مزوّر يعدّي، والـ cookie مش secure، والـ WebSocket بيتقفل بعد دقيقة، و 502 عشوائي من الـ keep-alive. بنفهم الـ reverse proxy، و TLS termination و Let's Encrypt و HSTS، و X-Forwarded-For و trust proxy صح، والـ hop-by-hop headers، والـ timeouts و 502 و 504، والـ WebSockets، و Caddy و nginx. والتحدي: proxy.js، reverse proxy حقيقي بـ node:http قصاد أربعتاشر حالة.

02 / شرح الدرس مكتوبًا

نص شرح المعلم والكود الذي كتبه ونتيجة تشغيله، بنفس ترتيب الدرس.

أهلًا بيك. في production الـ users مش بيكلّموا الـ container بتاع Node على طول. فيه حاجة قدامه اسمها reverse proxy: nginx، أو Caddy، أو Traefik، أو load balancer بتاع الـ cloud. بيعمل TLS: الـ HTTPS بيخلص عنده، والـ certificates عنده، و Node بيكلّمه HTTP عادي جُوّه الـ network الخاصة. بيوزّع على أكتر من container، ويشيل اللي واقع. بيعمل compression، وبيخدم static files، وبيحدّد حجم الـ body. وبيحمي Node من clients بطيئة، زي slowloris، لأنه مستحمل connections كتير مفتوحة أكتر من Node. بس أول ما تحط proxy، الـ app بيشوف حاجة غريبة. بص على server dot naive dot js. المشاكل: ورا proxy، req socket remoteAddress بقى الـ IP بتاع الـ proxy، لكل الـ users. فالـ rate limit بيعدّ كل الناس كأنهم واحد، وبعد مِيّة request يقفل الموقع كله. فالحل اللي بييجي في الدماغ: X-Forwarded-For، وناخد أول IP. بس الـ header ده الـ client بيكتبه زي ما هو عايز. يكتب أي IP، ويبقى user جديد كل request، والـ rate limit ملوش لازمة. و X-Forwarded-Proto بيتصدّق من أي حد. والـ app بيسمع على zero dot zero dot zero dot zero ومعمول له publish، فممكن حد يكلّمه على طول ويتعدّى الـ proxy خالص، ومعاه كل الـ headers اللي هو عايزها.

X-Forwarded-For بيشتغل إزاي؟ كل proxy بيضيف الـ IP اللي جاله منه في الآخر. فالـ header بيبقى list: أول واحد هو اللي الـ client قال عليه، وآخر واحد هو اللي آخر proxy شافه بعينه. اللي تقدر تصدّقه هو اللي ضافه proxy انت عارفه. فبتقرا من اليمين: الـ remote address، وبعدين اللي قبله، وتعدّي على كل IP بتاع proxy من بتوعك، وأول IP مش بتاعك هو الـ client. وأي حاجة شمال كده، الـ client هو اللي كاتبها، فما تتصدّقش. لو الـ remote نفسه مش proxy من بتوعك، يبقى الـ request جاي على طول، والـ header كله يترمي. والـ edge proxy، أول proxy في الطريق، الأحسن يكتب الـ header من الأول، مش يضيف على اللي الـ client بعته. في Express ده trust proxy: list من الـ CIDRs بتاعة الـ proxies، أو رقم: عدد الـ proxies اللي قدامك بالضبط. وكده req ip و req protocol و req secure بيبقوا صح. و true معناها صدّق أي حد، وده نفس المشكلة.

الـ hop-by-hop headers: فيه headers بتخص الـ connection الواحدة بين اتنين، مش الـ request كله. زي Connection، و Keep-Alive، و Transfer-Encoding، و Upgrade. الـ proxy لازم يشيلها وما يبعتهاش لقدام، لأن الـ connection بينه وبين الـ upstream connection تانية خالص. وأي header اسمه مكتوب جُوّه Connection بيتشال برضه. ودي حاجة مهمة للـ WebSockets: الـ WebSocket بيبدأ بـ Upgrade و Connection upgrade، ودول hop-by-hop. فلازم تقول للـ proxy صراحةً يعدّيهم، وإلا الـ WebSocket مش هيفتح خالص. وكمان الـ read timeout: nginx بيقفل أي connection ساكتة دقيقة، فالـ WebSocket لازم timeout أطول أو ping. و خمسمية واتنين bad gateway: الـ proxy مش عارف يكلّم الـ upstream، الـ container واقع أو بيعمل restart. و خمسمية وأربعة gateway timeout: الـ upstream ما ردّش في الوقت.

TLS: الـ certificate من Lets Encrypt ببلاش، وبيخلص بعد تلات شهور، فالتجديد لازم يبقى أوتوماتيك. Caddy بيعمل ده لوحده، و nginx محتاج certbot. TLS واحد اتنين وواحد تلاتة بس، والقديم يتقفل. HTTP على port تمانين بيعمل redirect لـ HTTPS. و HSTS: header بيقول للـ browser ما يكلّمنيش غير HTTPS لمدة سنة، فحتى لو حد كتب http، الـ browser مش هيبعت حاجة من غير تشفير. والـ cookies بـ secure. وده بيشتغل صح بس لو الـ app عارف إن الـ request الأصلي كان https، يعني X-Forwarded-Proto من proxy انت عارفه. ولو الـ network بين الـ proxy و Node مش بتاعتك لوحدك، زي بين datacenters، يبقى تشفّر هناك كمان. ده mTLS، ودرس الـ zero trust بعدين. دول الـ config الحقيقيين. Caddyfile: سطور قليلة، والـ TLS والتجديد أوتوماتيك، و reverse_proxy على الـ two containers بـ health check. nginx dot conf: الـ upstream فيه الاتنين، والـ redirect، والـ TLS، و HSTS، و client_max_body_size، لأن الـ default بتاعه ميجا واحدة بس وأي upload أكبر بيرجع four thirteen. و X-Forwarded-For هو remote_addr بس، علشان الـ edge يكتبه من الأول. والـ timeouts. و location slash ws فيه الـ Upgrade بإيدك. و server dot js: trust proxy على الـ loopback والـ network الداخلية بس. و keepAliveTimeout أطول من بتاع الـ proxy. لو Node قفل connection idle والـ proxy في نفس الوقت بعت عليها request، الـ proxy ياخد reset ويرجّع خمسمية واتنين. ودي من أشهر أسباب الـ خمسمية واتنين العشوائي ورا load balancers.

دلوقتي نكتب proxy بنفسنا، علشان نشوف كل ده بعينينا. lab dot js بيشغّل api حقيقي، و proxies حقيقيين قدامه، و client جاي من IP تاني على الـ loopback، مِيّة سبعة وعشرين صفر صفر خمسة. والـ client بيبعت X-Forwarded-For مزوّر، و header في Connection. وبعدين upstream بطيء، و upstream واقع، و Express بـ trust proxy ومن غيره. التحدي: اكتب proxy dot js. التفاصيل كلها في الكومنت، و test dot js فيه أربعتاشر حالة، كلها على servers حقيقية.

تمرين

proxy.js: normalizeIp و inCidr للـ IPv4 CIDRs، و clientIp من اليمين بالـ CIDRs أو بعدد الـ hops، و stripHopByHop و forwardHeaders بالـ edge، و createProxy بـ round robin و 502 و 504.

الحل. inCidr: الـ IP رقم، والـ mask بيخلّي الـ bits الأولانية بس، ونقارن. clientIp: loop من اليمين، وأول واحد مش trusted. سطرين بس، وهما الفرق بين rate limit شغّال و rate limit أي حد يعدّيه. forwardHeaders: الـ edge بيمسح أي X-Forwarded جاي من الـ client قبل ما يكتب. createProxy: http request للـ upstream، و pipe في الاتجاهين، و setTimeout يرجّع خمسمية وأربعة، و error يرجّع خمسمية واتنين. نشغّل lab dot js.

proxy.js
// proxy.js: reverse proxy حقيقي بـ node:http، والـ client IP الحقيقي ورا proxies، والـ headers اللي بتتشال وبتتضاف
import http from "node:http";
import net from "node:net";

export const normalizeIp = (ip) => String(ip ?? "").trim().replace(/^::ffff:(?=\d+\.\d+\.\d+\.\d+$)/, "");

const v4 = (ip) => ip.split(".").reduce((n, o) => n * 256 + Number(o), 0);

export function inCidr(ip, cidr) {
  ip = normalizeIp(ip);
  const [base, bits = net.isIPv4(base) ? "32" : "128"] = cidr.split("/");
  if (!net.isIPv4(ip) || !net.isIPv4(base)) return ip === normalizeIp(base); // IPv6: مقارنة مباشرة بس
  const b = Number(bits);
  const mask = b === 0 ? 0 : 2 ** 32 - 2 ** (32 - b);
  return (v4(ip) & mask) >>> 0 === (v4(base) & mask) >>> 0;
}

export function clientIp(xff, remote, trusted) {
  const chain = [...String(xff ?? "").split(",").map(normalizeIp).filter(Boolean), normalizeIp(remote)];
  if (typeof trusted === "number") return chain[Math.max(0, chain.length - 1 - trusted)];
  for (let i = chain.length - 1; i >= 0; i--) if (!trusted.some((c) => inCidr(chain[i], c))) return chain[i];
  return chain[0];
}

export const HOP_BY_HOP = ["connection", "keep-alive", "proxy-connection", "proxy-authenticate", "proxy-authorization", "te", "trailer", "transfer-encoding", "upgrade"];

export function stripHopByHop(headers) {
  const conn = Object.entries(headers).find(([k]) => k.toLowerCase() === "connection")?.[1];
  const named = String(conn ?? "").toLowerCase().split(",").map((s) => s.trim()).filter(Boolean);
  const out = {};
  for (const [k, v] of Object.entries(headers)) {
    const key = k.toLowerCase();
    if (!HOP_BY_HOP.includes(key) && !named.includes(key)) out[key] = v;
  }
  return out;
}

export function forwardHeaders(headers, { remote, proto = "http", edge = false }) {
  const h = stripHopByHop(headers);
  const ip = normalizeIp(remote);
  if (edge) { delete h["x-forwarded-for"]; delete h["x-forwarded-proto"]; delete h["x-forwarded-host"]; delete h["x-real-ip"]; delete h.forwarded; }
  h["x-forwarded-for"] = h["x-forwarded-for"] ? `${h["x-forwarded-for"]}, ${ip}` : ip;
  h["x-forwarded-proto"] ??= proto;
  if (headers.host) h["x-forwarded-host"] ??= headers.host;
  return h;
}

export function createProxy({ upstreams, timeoutMs = 5000, edge = true, proto = "http" }) {
  let next = 0;
  return http.createServer((req, res) => {
    const target = new URL(upstreams[next++ % upstreams.length]);
    const up = http.request({ host: target.hostname, port: target.port, method: req.method, path: req.url, headers: forwardHeaders(req.headers, { remote: req.socket.remoteAddress, proto, edge }) });
    const fail = (status, msg) => { if (!res.headersSent) { res.writeHead(status, { "content-type": "text/plain" }); res.end(msg); } else res.destroy(); };
    up.setTimeout(timeoutMs, () => { up.destroy(); fail(504, "gateway timeout"); });
    up.on("error", () => fail(502, "bad gateway"));
    up.on("response", (r) => { res.writeHead(r.statusCode, stripHopByHop(r.hea
…

المخرجات

$ node lab.js
مباشرة على الـ api من غير proxy، و header مزوّر: {"socket":"127.0.0.5","naive":"6.6.6.6","real":"127.0.0.5","proto":null,"secretHeader":"on"}
ورا proxy بيضيف على الـ header (edge: false):     {"socket":"127.0.0.1","naive":"6.6.6.6","real":"127.0.0.5","proto":"http","secretHeader":null}
ورا edge proxy بيكتب الـ header من الأول:         {"socket":"127.0.0.1","naive":"127.0.0.5","real":"127.0.0.5","proto":"https","secretHeader":null}
upstream أبطأ من الـ timeout: 504 gateway timeout
upstream واقع: 502 bad gateway
express و trust proxy false   : {"ip":"127.0.0.1","protocol":"http","secure":false}
express و trust proxy loopback: {"ip":"127.0.0.5","protocol":"https","secure":true}

مباشرة على الـ api: الـ naive صدّق الـ IP المزوّر، six six six six، والـ header اللي في Connection وصل. ودي الطريقة اللي حد بيتعدّى بيها الـ proxy لو الـ api مفتوح. ورا proxy بيضيف: الـ socket بقى الـ proxy. الـ naive لسه بياخد أول IP، فلسه مزوّر. أما clientIp من اليمين طلّع مِيّة سبعة وعشرين صفر صفر خمسة، الـ client الحقيقي. ورا الـ edge: الـ header اتكتب من الأول، فحتى الـ naive بقى صح، والـ proto بقى https. الـ slow: خمسمية وأربعة. والـ dead: خمسمية واتنين. و Express من غير trust proxy: الـ ip هو الـ proxy، والـ protocol http رغم إن الـ user على https، فـ secure false. ومع trust proxy loopback: الـ ip الحقيقي، و https، و secure true. ونشغّل test dot js.

proxy.js

المخرجات

$ node test.js
✅ normalizeIp: ::ffff: قبل IPv4 بس، والمسافات 
✅ inCidr: /8 و /32 و /0 و من غير bits 
✅ inCidr: /24 و /12 على الحدود 
✅ inCidr: IPv6 مقارنة مباشرة 
✅ clientIp: من اليمين، وأول IP مش trusted 
✅ clientIp: الـ remote مش trusted ← الـ header ما يتصدّقش خالص 
✅ clientIp: كلهم trusted ← أول واحد، وعدد hops زي trust proxy رقم 
✅ stripHopByHop: القايمة واللي في Connection، و lowercase 
✅ forwardHeaders: بيضيف على XFF، و proto و host لو مش موجودين 
✅ forwardHeaders: الـ edge بيشيل كل اللي الـ client بعته 
✅ createProxy: الـ status والـ path والـ body بيعدّوا، والـ edge بيكتب XFF من الأول 
✅ createProxy: round robin على الـ upstreams 
✅ createProxy: timeout ← 504، و upstream واقع ← 502 
✅ createProxy: POST بيعدّي، والـ response headers بتاعة الـ hop بتتشال 

14/14

كله نجح. الـ image شغّالة على الـ laptop. بس الـ laptop ممكن يكون Mac بـ ARM، والـ server x86، أو العكس. والـ native modules زي bcrypt و sharp متعملة لـ CPU معيّن. الدرس الجاي: الـ multi-arch images، و buildx والـ platforms، والـ native modules، و musl مقابل glibc.

server.naive.js
// server.naive.js: الـ api بيقرا الـ client IP من الـ header على طول، وبيثق في أي X-Forwarded-Proto
import express from "express";

const app = express();
const hits = new Map(); // rate limit بالـ IP

app.use((req, res, next) => {
  const ip = String(req.headers["x-forwarded-for"] ?? req.socket.remoteAddress).split(",")[0].trim(); // أي حد يكتب أي IP
  const n = (hits.get(ip) ?? 0) + 1;
  hits.set(ip, n);
  if (n > 100) return res.status(429).end();
  next();
});

app.post("/login", (req, res) => {
  const secure = req.headers["x-forwarded-proto"] === "https"; // حتى لو الـ request جاي من بره الـ proxy خالص
  res.cookie("sid", "abc", { secure, httpOnly: true });
  res.json({ ok: true });
});

app.listen(3000, "0.0.0.0"); // مفتوح للعالم، فالـ proxy ممكن يتعدّى
Caddyfile
# Caddyfile: TLS أوتوماتيك من Let's Encrypt، وتجديد لوحده، و HTTP ← HTTPS
shop.example.com {
    encode zstd gzip
    header Strict-Transport-Security "max-age=31536000; includeSubDomains"

    reverse_proxy api-1:3000 api-2:3000 {
        lb_policy round_robin
        health_uri /healthz
        health_interval 5s
        # Caddy بيكتب X-Forwarded-For من الأول، وبيبعت X-Forwarded-Proto و Host
    }
}
nginx.conf
# nginx.conf: نفس الكلام بـ nginx
upstream api {
    server api-1:3000 max_fails=3 fail_timeout=10s;
    server api-2:3000 max_fails=3 fail_timeout=10s;
    keepalive 32;
}

server {
    listen 80;
    server_name shop.example.com;
    return 301 https://$host$request_uri;          # HTTP ← HTTPS
}

server {
    listen 443 ssl;
    http2 on;
    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;
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

    client_max_body_size 10m;                     # الـ default واحد ميجا بس ← 413

    location / {
        proxy_pass http://api;
        proxy_http_version 1.1;
        proxy_set_header Connection "";           # keepalive مع الـ upstream
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $remote_addr;   # edge: من الأول، مش $proxy_add_x_forwarded_for
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_connect_timeout 2s;
        proxy_read_timeout 30s;                   # أطول من أطول request عادي، وأقصر من صبر الـ user
    }

    location /ws {
        proxy_pass http://api;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;   # Upgrade و Connection hop-by-hop، فلازم تتبعت بإيدك
        proxy_set_header Connection "upgrade";
        proxy_read_timeout 1h;                    # وإلا الـ WebSocket يتقفل بعد ستين ثانية سكوت
    }
}
server.js
// server.js: الـ api ورا proxy معروف بس
import express from "express";

const app = express();
// صدّق الـ X-Forwarded-* بس لو الـ request جاي من الـ proxy نفسه (الـ network بتاعة compose مثلًا)
app.set("trust proxy", ["loopback", "172.16.0.0/12"]); // أو رقم: عدد الـ proxies اللي قدامك بالضبط
// كده: req.ip هو الـ client الحقيقي، و req.protocol هو https لو الـ proxy قال كده، و req.secure

app.post("/login", (req, res) => {
  res.cookie("sid", "abc", { secure: true, httpOnly: true, sameSite: "lax" });
  res.json({ ok: true, ip: req.ip });
});

const server = app.listen(3000, "0.0.0.0"); // جُوّه الـ container، ومن غير ports: الـ proxy بس يوصله
// الـ keep-alive بتاع Node لازم يبقى أطول من بتاع الـ proxy، وإلا Node يقفل connection والـ proxy لسه بيبعت عليها ← 502 عشوائي
server.keepAliveTimeout = 65_000;
server.headersTimeout = 66_000;
lab.js
// lab.js: client و proxy و api حقيقيين على الـ loopback. الـ client جاي من 127.0.0.5، والـ proxy على 127.0.0.1
import http from "node:http";
import express from "express";
import { createProxy, clientIp } from "./proxy.js";

const listen = (srv) => new Promise((ok) => srv.listen(0, "127.0.0.1", () => ok(srv.address().port)));
const api = http.createServer((req, res) => {
  if (req.url === "/slow") return setTimeout(() => res.end("متأخر"), 1500);
  res.setHeader("content-type", "application/json");
  res.end(JSON.stringify({
    socket: req.socket.remoteAddress,
    naive: String(req.headers["x-forwarded-for"] ?? req.socket.remoteAddress).split(",")[0].trim(), // أول IP في الـ header
    real: clientIp(req.headers["x-forwarded-for"], req.socket.remoteAddress, ["127.0.0.1/32"]),
    proto: req.headers["x-forwarded-proto"] ?? null,
    secretHeader: req.headers["x-internal-debug"] ?? null,
  }));
});
const apiPort = await listen(api);
const proxyPort = await listen(createProxy({ upstreams: [`http://127.0.0.1:${apiPort}`], timeoutMs: 500, proto: "https" }));
const appendPort = await listen(createProxy({ upstreams: [`http://127.0.0.1:${apiPort}`], timeoutMs: 500, edge: false }));
const deadPort = await listen(createProxy({ upstreams: ["http://127.0.0.1:1"], timeoutMs: 500 }));

const get = (port, path, headers = {}) => new Promise((ok, no) =>
  http.get({ host: "127.0.0.1", port, path, headers, localAddress: "127.0.0.5", agent: false }, (r) => {
    let body = ""; r.on("data", (c) => (body += c)); r.on("end", () => ok({ status: r.statusCode, body }));
  }).on("error", no));

const spoof = { "x-forwarded-for": "6.6.6.6", connection: "x-internal-debug", "x-internal-debug": "on" };
console.log("مباشرة على الـ api من غير proxy، و header مزوّر:", (await get(apiPort, "/", spoof)).body);
console.log("ورا proxy بيضيف على الـ header (edge: false):    ", (await get(appendPort, "/", spoof)).body);
console.log("ورا edge proxy بيكتب الـ header من الأول:        ", (await get(proxyPort, "/", spoof)).body);
const slow = await get(proxyPort, "/slow");
console.log(`upstream أبطأ من الـ timeout: ${slow.status} ${slow.body}`);
const dead = await get(deadPort, "/");
console.log(`upstream واقع: ${dead.status} ${dead.body}`);

// نفس الحكاية بـ Express: من غير trust proxy كل الـ users ليهم IP واحد، IP الـ proxy
for (const trust of [false, "loopback"]) {
  const app = express();
  app.set("trust proxy", trust);
  app.get("/", (req, res) => res.json({ ip: req.ip, protocol: req.protocol, secure: req.secure }));
  const port = await listen(http.createServer(app));
  const front = await listen(createProxy({ upstreams: [`http://127.0.0.1:${port}`], proto: "https" }));
  console.log(`express و trust proxy ${String(trust).padEnd(8)}: ${(await get(front, "/", spoof)).body}`);
}
process.exit(0);
03 / دروس المسار (122)
  1. 01أهلًا بيك: يعني إيه backend، والكورس ده ماشي إزاي
  2. 02الملفات والفولدرات والـ terminal: تتحرك على جهازك زي المبرمجين
  3. 03Node وأول برنامج: تشغّل وتقرا رسالة الخطأ
  4. 04القيم والمتغيرات والأنواع: const و let و typeof
  5. 05القرارات والتكرار: if و === والـ truthy والـ loops
  6. 06الـ functions: parameters و return والـ arrow functions والـ scope
  7. 07الـ objects والـ arrays: destructuring و spread و map و filter و reduce
  8. 08JSON والنصوص والتواريخ: اللغة اللي الـ backend بيتكلم بيها
  9. 09الـ callbacks والـ closures وأول لمسة async: setTimeout
  10. 10الـ classes والـ errors: throw و try/catch و stack trace
  11. 11git: تحفَظ كل خطوة وترجعلها، وما تسرّبش أسرارك
  12. 12ESM و CommonJS و package exports: Node بيلاقي الـ modules إزاي
  13. 13الـ Promises و async/await: sequential و parallel والأخطاء والإلغاء
  14. 14الـ event loop والـ microtasks: Node بيعمل كل حاجة بـ thread واحد إزاي
  15. 15الـ Streams والـ Backpressure: ملفات أكبر من الـ memory
  16. 16الـ File System والـ paths: قراية وكتابة صح، ومن غير path traversal
  17. 17الـ Buffers والبيانات الـ binary: الـ bytes من جُوّه
  18. 18Crypto APIs والأسرار: random و hash و HMAC و AES-GCM
  19. 19الـ Worker Threads: شغل CPU تقيل من غير ما توقف الـ server
  20. 20الـ Errors على مستوى الـ process: operational و programmer و crash-only
  21. 21الـ Node test runner والـ debugging: tests من غير مكتبات
  22. 22HTTP من جُوّه: الـ methods والـ status codes والـ headers
  23. 23Express والـ Routing: الـ params والـ query والترتيب والـ Router
  24. 24Express 5: إيه اللي اتغيّر، وإزاي تنقل مشروع قديم
  25. 25الـ Middleware ودورة حياة الـ request جُوّه Express
  26. 26Validation للمدخلات بـ Zod: ما تصدّقش أي حاجة جاية من برة
  27. 27تصنيف الأخطاء و Problem Details: شكل واحد لكل الـ errors
  28. 28الـ CORS والـ Cookies: المتصفح بيحمي المستخدم إزاي
  29. 29رفع الملفات وحدود الحجم: multer من غير ثغرات
  30. 30الـ Realtime: Server-Sent Events و WebSockets
  31. 31OpenAPI والـ API Versioning: عقد مكتوب، وتغيير من غير ما تكسر حد
  32. 32PostgreSQL: الـ DDL وأنواع البيانات الصح لكل عمود
  33. 33الـ Constraints: PK و FK و UNIQUE و CHECK — الـ database بتحمي نفسها
  34. 34الـ Normalization والـ ERD: تقسّم البيانات صح من أول يوم
  35. 35الـ JOINs والتجميع: INNER و LEFT و GROUP BY و HAVING و window functions
  36. 36JSONB في PostgreSQL: إمتى تستخدمه، وإزاي تسأله وتعمله index
  37. 37الـ Indexes من جُوّه: B-tree و GIN و BRIN
  38. 38Partial و Covering indexes: indexes أصغر وأسرع
  39. 39الـ Pagination بالـ Keyset: ليه OFFSET بيبطّأ وبيكرر
  40. 40Full Text Search في Postgres: بحث بالكلمات، بالعربي والإنجليزي
  41. 41EXPLAIN ANALYZE والـ Planner: اقرا الـ plan وصلّح الـ query
  42. 42الـ Transactions بـ node-postgres: كله أو ولا حاجة
  43. 43الـ MVCC ومستويات الـ Isolation: transactions كتير في نفس الوقت
  44. 44الـ Locks والـ Deadlocks: مين مستني مين
  45. 45SKIP LOCKED و Job Leasing: Postgres كـ job queue
  46. 46Row Level Security والـ Multi-tenancy: كل عميل يشوف بياناته بس
  47. 47Migrations من غير downtime: expand و backfill و contract
  48. 48الـ Replication والـ Read Replicas: نسخ حية من الـ database
  49. 49الـ Connection Pooling و PgBouncer: connections أقل، requests أكتر
  50. 50الـ Partitioning والـ VACUUM والمراقبة: Postgres على المدى الطويل
  51. 51Redis وأنواع البيانات: strings و hashes و lists و sets و sorted sets
  52. 52Cache-aside والـ TTL: Redis قدام الـ database
  53. 53الـ Cache invalidation والـ Stampede: لما الـ key الشهير يموت
  54. 54Lua والـ Atomicity في Redis: كام أمر كأنهم أمر واحد
  55. 55الـ Rate Limiting في Redis: fixed window و sliding window و token bucket
  56. 56الـ Sessions في Redis: تسجيل دخول تقدر تلغيه في أي وقت
  57. 57Redis Streams والـ Consumer Groups: log بيتوزع على workers
  58. 58Pub/Sub و BullMQ: إشارات بين الـ instances، وطوابير شغل محترمة
  59. 59Redis لما يقع: الـ persistence (RDB و AOF) والـ replication
  60. 60Redis في الإنتاج: الـ ACLs والـ Cluster والمراقبة
  61. 61تخزين الـ Passwords بـ Argon2id: صح من أول مرة
  62. 62Session Fixation والـ Rotation: id جديد مع كل صلاحية جديدة
  63. 63الـ Cookie Flags والـ CSRF: المتصفح بيبعت الـ cookie، فلازم تتأكد مين اللي طلب
  64. 64الـ XSS والـ Content Security Policy: ما تخليش النص يتحوّل لـ code
  65. 65الـ OAuth و OpenID Connect و PKCE: login بـ Google من غير ما تشوف الـ password
  66. 66التأكد من الـ JWT صح: التوقيع والـ claims والـ scopes
  67. 67الـ BOLA و BFLA: الـ token السليم مش معناه إن الطلب مسموح
  68. 68الـ RBAC و ABAC والملكية: policy واحدة بدل ifs متفرقة
  69. 69الـ MFA والـ recovery: التاني factor اللي بجد
  70. 70الـ Passkeys و WebAuthn: login من غير password وضد الـ phishing
  71. 71الـ Threat Modeling بـ STRIDE: فكّر زي المهاجم قبل ما تكتب code
  72. 72الـ SQL Injection والـ Parameterization: الـ data ما تبقاش code
  73. 73الـ SSRF والـ Egress: ما تخليش الـ server بتاعك يزور الشبكة الداخلية
  74. 74الـ Security Headers والـ TLS: كل response يقول للـ browser يحمي المستخدم إزاي
  75. 75توقيع الـ Webhooks والـ Replay: الـ webhook ده جاي فعلًا من Stripe؟
  76. 76تغيير الأسرار والـ HMAC: إزاي تغيّر مفتاح من غير ما كل حاجة تقع
  77. 77سلسلة التوريد والـ SBOM: إنت واثق في ألف package ما تعرفهمش؟
  78. 78الـ Audit والـ Privacy Logs: سجّل مين عمل إيه، من غير ما تسجّل الأسرار
  79. 79اختبار الأمان و OWASP ASVS: الأمن جُوّه الـ CI، مش audit آخر السنة
  80. 80الـ Prototype Pollution والـ Resource DoS: request واحد يخلّي الكل admin أو يوقّع الـ server
  81. 81الـ SLI والـ SLO والـ Error Budget: إمتى تصحّي الـ on-call وإمتى تسيبه نايم
  82. 82الـ Event Loop Delay والـ ELU: لما Node نفسه يتخنق
  83. 83الـ Worker Threads والـ CPU Profiling: الشغل التقيل بره الـ event loop، والوقت راح فين
  84. 84الـ Memory والـ GC والـ Leaks: الـ process اللي بيكبر لحد ما يقع
  85. 85الـ N+1 Queries والـ DataLoader: صفحة واحدة بمِيّتين query
  86. 86الـ HTTP Caching والـ Compression: أسرع request هو اللي ما بيوصلش
  87. 87الـ Load Testing والـ Soak Testing: الـ server بيستحمل قد إيه بجد
  88. 88الـ Concurrency Limits والـ Bulkheads: dependency بطيئة ما توقعش الـ app كله
  89. 89الـ Timeouts والـ Retries والـ Circuit Breakers: إزاي تتعامل مع dependency بطيئة أو واقعة
  90. 90OpenTelemetry والـ Distributed Tracing: الـ request راح فين وضاع وقته فين
  91. 91Docker Layers والـ Build Context: ليه الـ build بياخد دقيقة وفيه الـ .env بتاعك
  92. 92Multi-stage Builds و Non-root: image صغيرة ومن غير root
  93. 93Docker Compose والـ Networks: الـ services بتلاقي بعض إزاي
  94. 94Docker Volumes: الـ data بتتحفَظ فين
  95. 95Healthcheck و Restart و Resource Limits: running مش معناها شغّال
  96. 96BuildKit والـ Build Secrets: الـ token ما يدخلش الـ image
  97. 97Reverse Proxy و TLS: اللي واقف قدام الـ appانت هنا
  98. 98Multi-arch Images والـ Native Modules: اشتغلت عندي
  99. 99CI/CD و Rollback و Canary: الـ deploy يبقى ممل
  100. 100Kubernetes من الأول: Pods و Deployments و Services و Probes
  101. 101Modular Monolith و DDD: حدود واضحة من غير network
  102. 102Transactional Outbox و Inbox: الـ event ما يضيعش وما يتنفذش مرتين
  103. 103Sagas: عملية واحدة على تلات services من غير transaction واحدة
  104. 104CQRS و Event Sourcing: الـ events هي الحقيقة
  105. 105الـ Queues والـ Brokers: BullMQ و RabbitMQ و Kafka و SQS
  106. 106Realtime على أكتر من instance: Redis pub/sub و presence و resume
  107. 107الـ Object Storage والـ Media: presigned URLs و uploads مباشرة
  108. 108التكامل مع الـ Email والـ Search: retries و webhooks و indexes
  109. 109GraphQL و gRPC في الإنتاج: contracts و DataLoader و limits و deadlines
  110. 110Kafka على أكتر من zone و region: الـ replication والـ ISR والـ acks والـ MirrorMaker والـ failover
  111. 111TypeScript و runtime boundaries: الـ types بتختفي، والـ schemas لأ
  112. 112ORM ولا SQL: query builders و repositories و migrations و transactions
  113. 113الـ Serverless من جُوّه: cold starts و freeze و timeouts و connections
  114. 114الـ Testing Pyramid: unit و integration و e2e على Postgres حقيقية
  115. 115Contract Testing و Property-Based Testing بـ fast-check
  116. 116الـ Payments: idempotency و webhooks و refunds و reconciliation
  117. 117الـ Timezones والـ Internationalization: UTC و DST والـ locales والـ plurals والفلوس
  118. 118الـ Privacy والـ Data Retention: consent و export و erasure و crypto shredding
  119. 119الـ ADRs والـ Feature Flags: القرارات المكتوبة والـ rollout من غير deploy
  120. 120Multi-region active-active: حل الـ conflicts بـ HLC و CRDTs
  121. 121Readiness و Liveness و Startup Probes: الـ pod جاهز ولا عايش؟
  122. 122Incident Runbooks: من الـ alert للـ postmortem