nginx + Web Servers
reverse proxy, rate limiting, security headers ו-SSL termination — ה-edge layer של כל תשתית production
תיאוריה
למה nginx חשוב ל-DevOps?
nginx הוא ה-web server הנפוץ ביותר בתשתיות production — לפי Netcraft הוא מריץ למעלה מ-34% מכלל הדומיינים הפעילים, כולל Netflix, Dropbox ו-GitHub. ל-DevOps engineer, nginx אינו רק web server: הוא ה-edge layer שדרכו עוברת כל תנועת HTTP, והוא מבצע בו-זמנית כמה תפקידים קריטיים:
- **Reverse proxy**: מקבל בקשות מהאינטרנט ומעביר אותן לשירותים פנימיים (Node.js, Python, Go, Docker containers)
- **Load balancer**: מחלק עומס בין מספר instances
- **SSL terminator**: מטפל ב-TLS כך שהאפליקציה מקבלת HTTP רגיל
- **Rate limiter**: מגן מפני DDoS ו-abuse
- **Security header injector**: מוסיף headers שמגנים על דפדפני המשתמשים
ב-job market של 2026, nginx מופיע ב-2.4 משרות מתוך כל 10 ב-DevOps — כמעט תמיד יחד עם Docker ו-Kubernetes (nginx Ingress Controller). כל DevOps engineer שעובד על VPS, ECS, או Kubernetes חייב לדעת לכתוב nginx config מאפס.
בפלטפורמה שלנו nginx רץ ב-production על devops.backbok.com ועושה בדיוק את הדברים האלה — ה-config ב-infrastructure/nginx/devops.backbok.com.conf הוא production-real.
reverse proxy בסיסי: proxy_pass, upstream block ו-proxy_set_header
הפעולה הנפוצה ביותר ב-nginx בסביבת production היא להעביר תנועה לשירות פנימי. הטעות הנפוצה של מפתחים שעוברים ל-DevOps היא להגדיר proxy_pass ישירות לכתובת IP בלי upstream block — זה עובד, אבל לא ניתן לתחזוקה ולא תומך ב-health checks עתידיים.
**מבנה production עם upstream:**
upstream backend {
server 127.0.0.1:3000;
keepalive 32;
}
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
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 5s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
}**למה כל header חשוב:**
- Host: $host — האפליקציה הפנימית מקבלת את שם הדומיין המקורי, לא 127.0.0.1. בלי זה, redirects יפנו לכתובת שגויה.
- X-Real-IP: $remote_addr — ה-IP האמיתי של הלקוח. בלי header זה, האפליקציה תראה תמיד את 127.0.0.1 כמקור הבקשה — שוברת logging, rate limiting אפליקטיבי, ו-geo-blocking.
- X-Forwarded-For: $proxy_add_x_forwarded_for — שרשרת כל ה-proxies דרכם עברה הבקשה.
- X-Forwarded-Proto: $scheme — http או https. גרמה לאינספור bugs שבהם האפליקציה לא ידעה שהמשתמש מחובר ב-HTTPS.
**location matching precedence — מקור נפוץ לבאגים:**
# = : exact match (highest priority)
location = /health { return 200 "ok"; }
# ^~ : prefix match, stops regex search
location ^~ /static/ { root /var/www; }
# ~ : case-sensitive regex
location ~ \.php$ { fastcgi_pass 127.0.0.1:9000; }
# none prefix: longest prefix match (lowest priority)
location /api/ { proxy_pass http://backend; }nginx בודק locations לפי הסדר: = > ^~ > regex (~, ~*) > prefix רגיל. רוב ה-502 errors שנראות כ-"nginx בעיה" הן בעצם location matching שגוי.
**proxy_http_version 1.1 + Connection: "" הם חובה כשמשתמשים ב-keepalive.** ברירת המחדל של nginx היא HTTP/1.0 ל-upstream, שלא תומך ב-keepalive — כל בקשה פותחת TCP connection חדש.
load balancing: upstream עם least_conn, ip_hash, ו-passive health check
כשיש יותר מ-instance אחד של שירות, ה-upstream block הופך ל-load balancer. nginx תומך במספר algorithms:
# Round-robin (ברירת מחדל) — לחלוקת עומס שווה:
upstream app_servers {
server 10.0.1.10:3000;
server 10.0.1.11:3000;
server 10.0.1.12:3000 backup;
}
# Least Connections — עדיף כשיש connections ארוכים (SSE, WebSocket, uploads):
upstream app_servers {
least_conn;
server 10.0.1.10:3000;
server 10.0.1.11:3000;
}
# IP Hash — sticky sessions (sessions לא distributed, או auth services):
upstream app_servers {
ip_hash;
server 10.0.1.10:3000;
server 10.0.1.11:3000;
}
# משקולות (weights) + passive health check:
upstream app_servers {
server 10.0.1.10:3000 weight=3 max_fails=3 fail_timeout=30s;
server 10.0.1.11:3000 weight=1 max_fails=3 fail_timeout=30s;
}**Passive health checks (nginx open-source):**
אם שרת מחזיר 3 שגיאות ב-30 שניות (max_fails=3 fail_timeout=30s), nginx מסיר אותו מהrotation למשך 30 שניות. זהו passive health check — nginx לא שולח בקשות בדיקה פעילות, רק לומד מכישלונות אמיתיים. Active health checks (health_check directive) זמינים רק ב-nginx Plus (commercial).
**WebSocket support:**
location /ws/ {
proxy_pass http://ws_backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 86400s;
}rate limiting: limit_req_zone, limit_req, burst ו-nodelay
כשאתר לוקח DDoS או scraper מריץ 500 בקשות בשנייה, השאלה הראשונה שמנהל ה-DevOps שואל היא "למה nginx לא throttle זה?". התשובה: כי אף אחד לא הגדיר limit_req_zone.
nginx מממש את **leaky bucket algorithm** ל-rate limiting — בקשות נכנסות לדלי, והדלי מתרוקן בקצב קבוע.
**הגדרת zones ב-http block (לא ב-server block!):**
http {
# zone לפי IP — 10MB memory, 100 req/sec per IP
limit_req_zone $binary_remote_addr zone=per_ip:10m rate=100r/s;
# zone מחמיר לlogin endpoint
limit_req_zone $binary_remote_addr zone=login_zone:5m rate=5r/m;
# zone גלובלי לכל ה-traffic
limit_req_zone $server_name zone=global:10m rate=5000r/s;
}**שימוש ב-zone בתוך location:**
server {
location /api/login {
# strict: 5 req/min per IP
limit_req zone=login_zone burst=3 nodelay;
limit_req_status 429;
proxy_pass http://backend;
}
location /api/ {
# burst=20: מאפשר spike של 20 בקשות נוספות
# nodelay: מגיש אותן מיד, לא מחכה
limit_req zone=per_ip burst=20 nodelay;
limit_req_status 429;
proxy_pass http://backend;
}
location /api/webhook {
# burst ללא nodelay: מחכה שהbucket יתפנה
limit_req zone=per_ip burst=50;
proxy_pass http://backend;
}
}**burst vs nodelay — ההבדל הקריטי:**
- burst=20 בלבד: nginx מחזיק עד 20 בקשות ב-queue ומגיש אותן לפי ה-rate. בקשות מעל 20 מקבלות 429. תגובה מתעכבת.
- burst=20 nodelay: nginx מגיש את ה-20 הבקשות הנוספות **מיד** (ללא delay), אבל ממשיך לספור אותן מול ה-rate. מתאים ל-API שצריך לטפל ב-legitimate spikes בלי לגרום ל-timeout.
**$binary_remote_addr vs $remote_addr:** גרסת ה-binary חוסכת זיכרון — IPv4 4 בייטים במקום עד 15 תווים. ב-10MB zone ניתן לאחסן כ-160,000 IPv4 addresses.
**Logging של בקשות שנחסמו:**
limit_req_log_level warn; # ברירת מחדל: errorconnection limiting: limit_conn_zone ו-limit_conn
בעוד limit_req מגביל את **קצב הבקשות**, limit_conn מגביל את **מספר החיבורים הפתוחים בו-זמנית**. זה רלוונטי במיוחד ל-download servers, long-polling APIs, Slowloris attack protection, ו-file uploads.
http {
# zone לפי IP
limit_conn_zone $binary_remote_addr zone=conn_per_ip:10m;
# zone לפי שרת
limit_conn_zone $server_name zone=conn_per_server:10m;
}
server {
location /downloads/ {
# מקסימום 5 connections מקביליים מאותו IP
limit_conn conn_per_ip 5;
limit_conn_status 503;
# גם rate limiting — כל connection לא יוריד מהר מדי
limit_rate 1m; # 1 MB/sec per connection
limit_rate_after 10m; # מהירות מלאה עד 10MB, אחר כך מוגבל
root /var/www/files;
}
location /api/ {
# מקסימום 100 connections מקביליים לכל ה-server
limit_conn conn_per_server 100;
limit_conn conn_per_ip 10;
proxy_pass http://backend;
}
}**שיטת הגנה משולבת (production best practice):**
http {
limit_req_zone $binary_remote_addr zone=per_ip_req:10m rate=100r/s;
limit_conn_zone $binary_remote_addr zone=per_ip_conn:10m;
}
server {
location / {
limit_conn per_ip_conn 20;
limit_req zone=per_ip_req burst=50 nodelay;
limit_req_status 429;
limit_conn_status 503;
proxy_pass http://backend;
}
}**Slowloris protection עם client timeouts:**
client_body_timeout 10s;
client_header_timeout 10s;
keepalive_timeout 65s;
send_timeout 10s;**ההבדל בין סטטוסים:** המוסכמה היא:
- Rate limit exceeded: **429 Too Many Requests** (RFC 6585)
- Connection limit exceeded: **503 Service Unavailable**
security headers: HSTS, CSP, X-Frame-Options ו-X-Content-Type-Options
כל security scanner (OWASP ZAP, Mozilla Observatory, Qualys SSL Labs) יסמן את ה-site כ-vulnerable אם חסרים security headers. nginx הוא המקום הנכון להוסיף אותם — פעם אחת לכל ה-sites, ללא שינוי קוד האפליקציה.
server {
listen 443 ssl;
server_name example.com;
# HSTS — מכריח HTTPS גם אם המשתמש מקליד http://
# max-age=31536000 = שנה אחת (המינימום המומלץ ל-preloading)
# includeSubDomains: חל גם על sub-domains
# preload: מאפשר כניסה ל-HSTS preload list של Chrome/Firefox
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
# Clickjacking protection
# SAMEORIGIN: מאפשר iframe רק מאותו origin
# DENY: אוסר iframe לחלוטין
add_header X-Frame-Options "SAMEORIGIN" always;
# MIME sniffing protection — דפדפן לא יניח שקובץ txt הוא JavaScript
add_header X-Content-Type-Options "nosniff" always;
# Referrer policy
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# Permissions policy
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
# Content Security Policy — מגדיר מהיכן מותר לטעון resources
# default-src 'self': כל resource חייב להגיע מאותו origin
# frame-ancestors 'none': CSP2 replacement ל-X-Frame-Options DENY
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self'; frame-ancestors 'none'" always;
location / {
proxy_pass http://backend;
}
}**ה-keyword always הוא קריטי:** בלי always, nginx לא יוסיף את ה-header לתשובות שגיאה (4xx, 5xx). זה יוצר מצב שבו דף 404 נשלח ללא HSTS, ותוקף יכול ל-redirect משם ל-HTTP.
**HSTS — ההשלכות לטווח ארוך:**
- לאחר שהדפדפן מקבל HSTS header, הוא לא יפנה בקשות HTTP עוד לאתר הזה למשך max-age שניות
- אם ה-SSL certificate יפג תוקפו בתקופה זו, המשתמשים לא יוכלו לגשת לאתר כלל
- התחילו עם max-age=300 (5 דקות) ב-staging, הגדילו בהדרגה ל-31536000 ב-production
**CSP עבור אפליקציות React/Vue (SPA):**
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; connect-src 'self' https://api.example.com" always;'unsafe-eval' נדרש לרוב ה-bundlers ב-development mode. ב-production, השתמשו ב-nonce או hash.
SSL termination ו-HTTP→HTTPS redirect
nginx הוא SSL termination point הנפוץ ביותר בתשתיות VPS ו-Docker. nginx מטפל ב-TLS handshake, מפענח את ה-traffic, ומעביר פנימה HTTP רגיל — האפליקציה לא צריכה לדעת שום דבר על TLS.
**הגדרת SSL שלמה עם certbot:**
# HTTP to HTTPS redirect — חייב להיות block נפרד!
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
# Let's Encrypt challenge path
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
# כל שאר ה-traffic
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name example.com www.example.com;
# certbot מציב cert ו-key כאן:
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# Mozilla Intermediate: TLSv1.2 + TLSv1.3
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305;
ssl_prefer_server_ciphers off;
# Session resumption — מפחית TLS handshake overhead
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;
# OCSP Stapling — מוודא certificate validity ישירות ב-handshake
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
location / {
proxy_pass http://backend;
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 https;
}
}**return 301 vs return 302:** תמיד השתמשו ב-301 (Permanent). דפדפנים מ-cache אותו לנצח — redirect אחד פחות בכל ביקור עתידי.
**certbot auto-renewal hook:**
# /etc/letsencrypt/renewal-hooks/post/reload-nginx.sh
#!/bin/bash
systemctl reload nginxכאשר certbot מחדש certificate, הוא מריץ את ה-hook — nginx טוען את ה-cert החדש ללא downtime.
troubleshooting: 502, 504, nginx -t ו-error_log debug
רוב ה-nginx incidents ניתן לפתור תוך דקות אם יודעים לקרוא את השגיאות הנכונות.
**502 Bad Gateway — nginx קיבל תגובה לא תקינה מה-backend:**
סיבות נפוצות:
1. Backend לא רץ: systemctl status myapp
2. Backend על port שגוי: ss -tlnp | grep 3000
3. Unix socket לא קיים: ls -la /run/myapp.sock
4. Backend מחזיר response לא תקין (header שגוי)
# בדיקת backend ישירה
curl -v http://127.0.0.1:3000/health
# בדיקת ה-socket
ls -la /run/myapp.sock
# קריאת nginx error log
tail -f /var/log/nginx/error.log**504 Gateway Timeout — nginx לא קיבל תגובה בזמן:**
location /api/slow-export {
proxy_connect_timeout 10s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;
proxy_pass http://backend;
}ברירת המחדל של proxy_read_timeout היא **60 שניות**. מי שרץ רפורטים ארוכים / export jobs צריך להגדיל.
**nginx -t — חייב לרוץ לפני כל reload:**
# בדיקת תקינות config ללא restart
nginx -t
# nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
# nginx: configuration file /etc/nginx/nginx.conf test is successful
# reload ללא downtime
systemctl reload nginx
# או:
nginx -s reload**error_log debug — לפיתרון בעיות עמוקות:**
# ב-http block (production):
error_log /var/log/nginx/error.log warn;
# עבור server ספציפי בלבד:
server {
error_log /var/log/nginx/debug.log debug;
}debug log כותב **כל** בקשה בפירוט מלא — לא להשאיר ב-production כי מלא את הdisk תוך דקות.
**access log ניתוח מהיר:**
# 10 ה-IPs הנפוצים ביותר
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -10
# כל ה-5xx errors
grep ' 5[0-9][0-9] ' /var/log/nginx/access.log | tail -50**logrotate עבור nginx:**
# /etc/logrotate.d/nginx
/var/log/nginx/*.log {
daily
missingok
rotate 52
compress
delaycompress
notifempty
create 0640 www-data adm
sharedscripts
postrotate
nginx -s reopen
endscript
}תרגול מעשי
1. אימות קינפוג nginx reverse proxy
צור קובץ קינפוג nginx עם upstream block תקני ו-location block שכולל את כל ה-directives הנדרשים ל-reverse proxy בproduction: upstream עם keepalive, proxy_pass, proxy_http_version 1.1, proxy_set_header X-Real-IP ו-proxy_set_header X-Forwarded-Proto. אמת שכל directive קיים.
2. פרסור ואימות קינפוג rate limiting
צור קובץ קינפוג nginx עם rate limiting תקני הכולל: limit_req_zone עם $binary_remote_addr, burst עם ערך לפחות 10, nodelay ו-limit_req_status 429. אמת שכל directive קיים ושערך ה-burst עומד בדרישה המינימלית.
3. בדיקת security headers בקינפוג nginx
צור קובץ קינפוג nginx עם ארבעת security headers הנדרשים: Strict-Transport-Security עם max-age לפחות 31536000 ו-always, X-Frame-Options, X-Content-Type-Options nosniff ו-Content-Security-Policy עם default-src. אמת כל header ואת ערך ה-max-age.
שאלות חיבור
חבר את מה שלמדת בנושא זה לנושאים קודמים. אין תשובה אחת נכונה — חשיבה ביקורתית היא המטרה.
שאלת חיבור 1
nginx רץ כ-systemd service על שרת ה-VPS. הסבר שלושה נושאים ש-Linux Advanced לימד שמחוברים ישירות לניהול nginx בproduction: (1) כיצד מנהלים את nginx כ-systemd service ומבצעים reload בלי downtime, (2) כיצד logrotate מונע מ-/var/log/nginx/access.log למלא את הdisk, ו-(3) כיצד fail2ban משתלב עם nginx כדי לבלום IPs שמייצרים הרבה שגיאות 4xx.
שאלת חיבור 2
בarchitecture שלך, nginx רץ בתוך Docker container ומשמש כ-reverse proxy מול containers אחרים של ה-application stack. הסבר: (1) כיצד מגדירים nginx כ-service ב-docker-compose.yml עם upstream שמצביע לcontainer שם (לא IP), (2) מה הרשת הפנימית ב-Docker שמאפשרת resolving של container names, ו-(3) מה השינוי הנדרש ב-nginx config כשה-backend הוא Docker container לעומת process שרץ ישירות על ה-host.
מוכן לבחינה?
בצע הערכה תיאורטית, תרגול CLI ושאלות חיבור כדי לסיים את הנושא.