תצורת Nginx: אופטימיזציה ואבטחה
בעיה: Nginx ברירת מחדל לא יכול להתמודד עם העומס
לקוח הגיע עם פרויקט Laravel 10 + PHP 8.3 טיפוסי שקרס תחת 1000 בקשות במקביל. Nginx מהמאגר נתן שגיאות 502 תכופות, וקבצים סטטיים נטענו תוך 3–5 שניות. גילינו ש-# /etc/nginx/sites-available/myapp.conf server { listen 80; server_name example.com www.example.com; return 301 https://$host$request_uri; } server { listen 443 ssl http2; server_name example.com www.example.com; root /var/www/myapp/current/public; index index.php; # SSL ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384; ssl_prefer_server_ciphers off; ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; # Заголовки безопасности add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; add_header X-Frame-Options "DENY" always; add_header X-Content-Type-Options "nosniff" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always; charset utf-8; client_max_body_size 50M; location / { try_files $uri $uri/ /index.php?$query_string; } location ~ \.php$ { fastcgi_pass unix:/var/run/php/php8.3-fpm.sock; fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; include fastcgi_params; fastcgi_buffers 16 16k; fastcgi_buffer_size 32k; fastcgi_read_timeout 300; } # Статика — максимальное кеширование location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg|woff2?|ttf|eot)$ { expires 1y; add_header Cache-Control "public, immutable"; access_log off; } # Запретить доступ к скрытым файлам location ~ /\. { deny all; access_log off; log_not_found off; } } ו-fastcgi buffers היו בברירת מחדל, מטמון לא הוגדר, והגבלת קצב לא הייתה קיימת. לאחר אופטימיזציה עמוקה — מטמון אגרסיבי, כוונון SSL והגבלת קצב — זמן ההשהיה P99 ירד מ-5 שניות ל-200 אלפיות השנייה, והשרת כעת מתמודד עם 12,000 בקשות בשנייה. להלן הגדרות מוכחות בייצור שאנו מיישמים על כל הפרויקטים. צרו קשר לבדיקה.
אנחנו לא רק מעתיקים תצורות גנריות; אנו מתאימים אותן למחסנית הספציפית שלכם. עבור חלק, כוונון עדין של upstream nodejs_app { server 127.0.0.1:3000; server 127.0.0.1:3001; keepalive 32; } server { listen 443 ssl http2; location / { proxy_pass http://nodejs_app; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; 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_cache_bypass $http_upgrade; proxy_read_timeout 300; } } לליבות CPU עובד הכי טוב; עבור אחרים, הפעלת # /etc/nginx/conf.d/gzip.conf gzip on; gzip_vary on; gzip_proxied any; gzip_comp_level 6; gzip_min_length 1024; gzip_types text/plain text/css text/xml text/javascript application/json application/javascript application/xml+rss application/atom+xml image/svg+xml font/ttf font/otf; # Proxy cache (для кеширования ответов бэкенда) proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=app_cache:10m max_size=1g inactive=60m use_temp_path=off; location /api/public/ { proxy_cache app_cache; proxy_cache_valid 200 10m; proxy_cache_use_stale error timeout updating; add_header X-Cache-Status $upstream_cache_status; proxy_pass http://app; } ו-limit_req_zone $binary_remote_addr zone=api:10m rate=30r/m; limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m; location /api/ { limit_req zone=api burst=10 nodelay; limit_req_status 429; proxy_pass http://app; } location /login { limit_req zone=login burst=2 nodelay; proxy_pass http://app; } היא המפתח. הדבר החשוב הוא לזהות היכן נמצא הצוואר בקבוק: דיסק, רשת או backend.
בעיות שאנו פותרים
- טעינת עמודים איטית עקב טיפול לא יעיל בקבצים סטטיים ומחסור במטמון. לדוגמה, הגשת CSS/JS ללא gzip וללא כותרות
log_format combined_json escape=json '{' '"time":"$time_iso8601",' '"remote_addr":"$remote_addr",' '"method":"$request_method",' '"uri":"$request_uri",' '"status":$status,' '"request_time":$request_time,' '"bytes_sent":$bytes_sent,' '"http_referer":"$http_referer",' '"http_user_agent":"$http_user_agent"' '}'; access_log /var/log/nginx/access.log combined_json; error_log /var/log/nginx/error.log warn;. - קריסות תחת עומס עקב timeouts ו-fastcgi buffers לא מוגדרים כראוי. סיבה נפוצה היא
nginx -tכשמצפים ל-10k RPS. - דליפות זיכרון מהגדרות
nginx -s reloadו-ab -n 1000 -c 100 https://example.com/גרועות. על VPS עם 2 GB RAM, עדיף להגדירwrk -t 4 -c 100 -d 10s https://example.com/או להגביל ידנית ל-2 תהליכים. - התקפות DDoS על APIs או דפי התחברות — ללא הגבלת קצב, אפילו בוטנט פשוט יכול להפיל את השרת. אנו משתמשים ב-zones הממופתחות לפי
limit_req_status 429עם מגבלות כמו 30/5 בקשות לדקה. - תצורה לא מאובטחת:
nginx -tחשוף, הגדרות SSL חלשות, חוסר HSTS.
תצורת בסיס ל-Laravel/PHP
אנו משתמשים בתבנית זו עבור 80% מפרויקטי PHP כנקודת התחלה.
# /etc/nginx/sites-available/myapp.conf
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name example.com www.example.com;
root /var/www/myapp/current/public;
index index.php;
# SSL
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
# Заголовки безопасности
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
charset utf-8;
client_max_body_size 50M;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php8.3-fpm.sock;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_buffers 16 16k;
fastcgi_buffer_size 32k;
fastcgi_read_timeout 300;
}
# Статика — максимальное кеширование
location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg|woff2?|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
# Запретить доступ к скрытым файлам
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
} הגדרות נוספות
Reverse Proxy ל-Node.js
אם ה-backend הוא Node.js, החליפו את fastcgi ב-proxy_pass:
upstream nodejs_app {
server 127.0.0.1:3000;
server 127.0.0.1:3001;
keepalive 32;
}
server {
listen 443 ssl http2;
location / {
proxy_pass http://nodejs_app;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
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_cache_bypass $http_upgrade;
proxy_read_timeout 300;
}
} Gzip ומטמון
קובץ נפרד עבור gzip ו-proxy_cache:
# /etc/nginx/conf.d/gzip.conf
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_min_length 1024;
gzip_types text/plain text/css text/xml text/javascript application/json application/javascript application/xml+rss application/atom+xml image/svg+xml font/ttf font/otf;
# Proxy cache (для кеширования ответов бэкенда)
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=app_cache:10m max_size=1g inactive=60m use_temp_path=off;
location /api/public/ {
proxy_cache app_cache;
proxy_cache_valid 200 10m;
proxy_cache_use_stale error timeout updating;
add_header X-Cache-Status $upstream_cache_status;
proxy_pass http://app;
} הגבלת קצב
הגנו על APIs והתחברות:
limit_req_zone $binary_remote_addr zone=api:10m rate=30r/m;
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;
location /api/ {
limit_req zone=api burst=10 nodelay;
limit_req_status 429;
proxy_pass http://app;
}
location /login {
limit_req zone=login burst=2 nodelay;
proxy_pass http://app;
} רישום (Logging)
פורמט JSON לשילוב עם מערכות ניטור:
log_format combined_json escape=json '{
'"time":"$time_iso8601",
'"remote_addr":"$remote_addr",
'"method":"$request_method",
'"uri":"$request_uri",
'"status":$status,
'"request_time":$request_time,
'"bytes_sent":$bytes_sent,
'"http_referer":"$http_referer",
'"http_user_agent":"$http_user_agent"
'}';
access_log /var/log/nginx/access.log combined_json;
error_log /var/log/nginx/error.log warn; בדיקת תצורה
פקודות פשוטות לאימות:
| פקודה | מטרה |
|---|---|
ab -n 1000 -c 100 |
אימות תחביר |
curl -I https://example.com | grep -i strict |
טעינה מחדש ללא השבתה |
| `nginx -T | grep server_name` |
ab -n 1000 -c 100 https://example.com/ |
בדיקת עומס |
wrk -t 4 -c 100 -d 10s https://example.com/ |
חלופה ל-ab |
השוואה לפני/אחרי
| פרמטר | ברירת מחדל | מותאם |
|---|---|---|
| זמן השהיה P99 | 5 שניות | 200 אלפיות השנייה |
| תפוקה | 500 RPS | 12,000 RPS |
| שימוש במעבד | 90% | 45% |
| גודל סטטי | לא דחוס | gzip רמה 6 |
התהליך שלנו
- בדיקת תצורה נוכחית — סקירת קבצי לוג, עומס, זיהוי צווארי בקבוק.
- עיצוב ארכיטקטורה — בחירת הסכמה (reverse proxy, עצמאי, עם upstream).
- יישום — הגדרת virtual hosts, SSL (Let's Encrypt או התעודה שלכם), הגבלת קצב, מטמון, gzip, כותרות אבטחה, רישום, אופטימיזציית עובדים.
- בדיקה — בדיקת עומס (ab, wrk), בדיקת אבטחה (SSL Labs), ניתוח לוגים.
- תיעוד — דיאגרמת תצורה, פקודות ניהול, הוראות עדכון.
מה כלול
- תיעוד תצורה מלא עם הסברים על כל פרמטר.
- סקריפטים לאוטומציה של פריסה (Ansible או Docker Compose).
- הגדרת ניטור והתראות (Prometheus + Alertmanager או מקביל).
- הדרכה לצוות שלכם: איך לבצע שינויים, להפעיל מחדש ולנתח לוגים.
- אחריות ל-30 יום על יציבות התצורה עם תמיכה.
מידע נוסף על בחירת worker_processes
בשרת עם 4 ליבות CPU, אופטימלי להגדיר `worker_processes auto;` (nginx יקבע את המספר). אבל אם היישום צורך הרבה זיכרון, אפשר להגביל ל-2 תהליכים. נוסחה: מספר ליבות CPU + 1 לפרויקטים כבדים.הניסיון שלנו
אנחנו עובדים עם Nginx למעלה מ-8 שנים. במשך הזמן הזה, הגדרנו יותר מ-100 שרתי ייצור לפרויקטים החל מדפי נחיתה ועד פלטפורמות מסחר אלקטרוני עם תעבורה גבוהה. עבור כל פרויקט, אנו מספקים תיעוד ואחריות ל-30 יום על יציבות התצורה.
לפי התיעוד הרשמי של Nginx, כוונון נכון ברמת מערכת ההפעלה (sysctl) והגדרות עובדים יכול להגדיל את התפוקה בעד 50%.
איך להגדיר הגבלת קצב להגנה מפני DDoS?
כל תרחיש מקבל zone משלו. עבור APIs, אנו משתמשים במגבלה של 30 בקשות לדקה לכל IP עם burst של 10. עבור התחברות, 5 בקשות לדקה עם burst של 2. תמיד הגדירו limit_req_status 429 ותעדו בקשות שנדחו. שלבו עם סינון גיאוגרפי ו-fail2ban. גישה זו מתמודדת אפילו עם התקפות DDoS בסיסיות.
למה SSL משפיע על Core Web Vitals?
לחיצת היד של SSL משפיעה ישירות על LCP ו-TTFB. אם משתמשים בצפנים חלשים (TLS 1.0) או חסר OCSP Stapling, זמן לחיצת היד יכול להגיע ל-300 אלפיות השנייה. אנו משתמשים רק ב-TLS 1.2/1.3, צפנים מודרניים (ECDHE+AES-GCM), ומפעילים OCSP Stapling. זה מפחית את TTFB ב-15–20% ללא עלות נוספת.
בדיקת תצורה שלב אחר שלב
- הריצו
nginx -tכדי לאמת תחביר. - צרו עומס עם
ab -n 1000 -c 100ועקבו אחר סטטוסי התגובה. - בדקו כותרות אבטחה באמצעות curl:
curl -I https://example.com | grep -i strict. - בדקו הגבלת קצב: שלחו 100 בקשות בדקה אחת וודאו ש-429 מופיע לאחר חריגה מהמגבלה.
קבלו ייעוץ ממהנדס — נעריך את הפרויקט שלכם ונציע תוכנית תצורת Nginx אופטימלית. צרו קשר לבדיקת התצורה הנוכחית שלכם.







