Nginx Production Configuration: SSL, Caching, and Security

Nginx production configuration guide: SSL termination, HTTP/2, caching headers, security headers, rate limiting, reverse proxy setup, and Cloudflare integration patterns.

E
ECOSIRE Research and Development Team
|19 مارس 20268 دقائق قراءة1.7k كلمات|

جزء من سلسلة Performance & Scalability

اقرأ الدليل الكامل

تكوين إنتاج Nginx: SSL والتخزين المؤقت والأمان

Nginx هو خادم الويب الإنتاجي الذي يتعامل تقريبًا مع جميع عمليات نشر الويب ذات حركة المرور العالية. عندما يتم تكوينه بشكل جيد، فإنه يوفر إنهاء SSL وخدمة ملفات ثابتة فعالة ووكيل WebSocket وتحديد المعدل ورؤوس الأمان - كل ذلك قبل أن يصل طلب واحد إلى تطبيق Node.js الخاص بك. عندما يتم تكوينه بشكل سيء، فإنه يصبح عنق الزجاجة، أو مسؤولية أمنية، أو سببًا لحلقات إعادة توجيه Cloudflare الغامضة.

يغطي هذا الدليل تكوين Nginx على مستوى الإنتاج لنشر Node.js متعدد التطبيقات: الواجهة الأمامية لـ Next.js وNestJS API وDocusaurus docs - جميعها تعمل على نفس الخادم خلف Cloudflare.

الوجبات الرئيسية

  • لا تقم مطلقًا بتقسيم www وغير www إلى كتل خادم منفصلة خلف Cloudflare - مما يؤدي إلى تكرار حلقات إعادة التوجيه
  • استخدم ملف تكوين Nginx واحدًا في /etc/nginx/conf.d/ — ولا تقم أيضًا بالارتباط الرمزي في sites-enabled/
  • تم إهمال X-XSS-Protection — استخدم CSP بدلاً من ذلك؛ X-Frame-Options: DENY لا يزال صالحًا
  • proxy_pass يجب أن يتضمن شرطة مائلة زائدة عند استخدام بادئة المسار
  • ضغط Gzip للنص/* أنواع MIME يستحق التمكين دائمًا
  • يتطلب تحديد المعدل تحديد limit_req_zone على مستوى http{}، وليس على مستوى server{}
  • دعونا نقوم بتشفير الشهادات التي يتم تجديدها تلقائيًا باستخدام Certbot؛ أضف مهمة cron لإعادة تحميل Nginx بعد التجديد
  • يتطلب وكيل WebSocket رؤوسًا محددة: Upgrade وConnection

هيكل الدليل

حافظ على تنظيم إعدادات Nginx:

/etc/nginx/
  nginx.conf                    — Main config (rarely touch this)
  conf.d/
    ecosire-production.conf     — All your server blocks in ONE file
  snippets/
    ssl-params.snippet          — SSL hardening (included by server blocks)
    proxy-params.snippet        — Common proxy headers
    security-headers.snippet    — Security headers

هام: ضع كل التكوينات الخاصة بك في conf.d/ecosire-production.conf. لا تقم أيضًا بإضافته إلى sites-enabled/ — فهذا يجعل Nginx يعالج التكوين مرتين، مما يؤدي إلى تكرار أخطاء limit_req_zone وسلوك غير متوقع.


مناطق تحديد الأسعار

حدد مناطق تحديد المعدل على مستوى http{} في التكوين الخاص بك. لا يمكن تعريفها داخل كتل server{}:

# /etc/nginx/conf.d/ecosire-production.conf

# Must be at http{} level — these are in the main conf or conf.d root
limit_req_zone $binary_remote_addr zone=api_general:10m rate=60r/m;
limit_req_zone $binary_remote_addr zone=api_auth:10m rate=10r/m;
limit_req_zone $binary_remote_addr zone=api_public:10m rate=30r/m;
limit_req_zone $binary_remote_addr zone=static_files:10m rate=200r/m;

كتلة خادم التطبيق الرئيسي

# Main web application — Next.js on port 3000
server {
    listen 80;
    listen [::]:80;
    server_name ecosire.com www.ecosire.com;

    # Cloudflare handles SSL termination — Nginx only sees HTTP
    # (If direct SSL, add listen 443 ssl and certificate paths)

    # Security headers
    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;
    add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
    # CSP — adjust based on your needs
    add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' https://js.stripe.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; frame-src https://js.stripe.com; connect-src 'self' https://api.ecosire.com;" always;

    # Remove server version from responses
    server_tokens off;

    # Gzip compression
    gzip on;
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_types
      text/plain
      text/css
      text/javascript
      application/javascript
      application/json
      application/xml
      image/svg+xml
      font/woff2;
    gzip_min_length 1024;

    # ─── Static Files: Next.js build output ─────────────────────────
    location /_next/static/ {
        proxy_pass http://127.0.0.1:3000;
        add_header Cache-Control "public, max-age=31536000, immutable";
        # Files include content hash in filename — safe to cache forever
    }

    # ─── Public static assets ────────────────────────────────────────
    location /assets/ {
        proxy_pass http://127.0.0.1:3000;
        add_header Cache-Control "public, max-age=86400";  # 1 day
    }

    # ─── Well-known files (no locale prefix) ─────────────────────────
    location /.well-known/ {
        proxy_pass http://127.0.0.1:3000;
        add_header Cache-Control "public, max-age=86400";
    }

    # ─── App routes (rate limited) ───────────────────────────────────
    location / {
        limit_req zone=api_general burst=20 nodelay;
        limit_req_status 429;

        proxy_pass http://127.0.0.1:3000;
        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;

        # Timeouts
        proxy_connect_timeout 60s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;
    }
}

كتلة خادم API

# NestJS API — port 3001
server {
    listen 80;
    listen [::]:80;
    server_name api.ecosire.com;

    server_tokens off;

    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;

    # CORS — handled by NestJS, but Nginx can add preflight response
    # for performance (avoids reaching Node.js for OPTIONS)
    location / {
        if ($request_method = 'OPTIONS') {
            add_header Access-Control-Allow-Origin "https://ecosire.com";
            add_header Access-Control-Allow-Methods "GET, POST, PUT, PATCH, DELETE, OPTIONS";
            add_header Access-Control-Allow-Headers "Content-Type, Authorization";
            add_header Access-Control-Allow-Credentials "true";
            add_header Access-Control-Max-Age 1728000;
            add_header Content-Length 0;
            return 204;
        }

        limit_req zone=api_general burst=30 nodelay;

        proxy_pass http://127.0.0.1:3001;
        proxy_http_version 1.1;
        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 60s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;
    }

    # Stricter rate limits for auth endpoints
    location ~ ^/api/auth/(login|exchange|callback) {
        limit_req zone=api_auth burst=5 nodelay;
        limit_req_status 429;

        proxy_pass http://127.0.0.1:3001;
        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;
    }

    # Health check — no rate limiting
    location /api/health {
        proxy_pass http://127.0.0.1:3001;
        proxy_set_header Host $host;
        access_log off;  # Don't log health check spam
    }

    # Stripe webhook — needs raw body
    location /api/billing/webhook {
        proxy_pass http://127.0.0.1:3001;
        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;
        # No rate limiting on webhooks — Stripe IPs are trusted
        # NestJS validates the Stripe signature
    }
}

وكيل WebSocket

إذا كان تطبيقك يستخدم WebSockets (Socket.IO، بوابة NestJS WebSocket)، فإن تكوين الوكيل يحتاج إلى رؤوس محددة:

# WebSocket upgrade handling
location /ws/ {
    proxy_pass http://127.0.0.1:3001;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "Upgrade";  # Capital U required
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;

    # WebSocket connections can be long-lived
    proxy_read_timeout 3600s;
    proxy_send_timeout 3600s;
}

بدون رؤوس Upgrade وConnection، تفشل مصافحة WebSocket للمتصفح مع ظهور خطأ 426 Upgrade Required أو 101 Switching Protocols.


SSL مباشر (بدون Cloudflare)

إذا لم تكن تستخدم Cloudflare، تعامل مع إنهاء SSL في Nginx مباشرة:

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    http2 on;
    server_name ecosire.com;

    ssl_certificate /etc/letsencrypt/live/ecosire.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/ecosire.com/privkey.pem;

    # SSL hardening
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers off;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 1d;
    ssl_stapling on;
    ssl_stapling_verify on;
    resolver 8.8.8.8 8.8.4.4 valid=300s;

    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;

    # ... rest of server block
}

# HTTP to HTTPS redirect
server {
    listen 80;
    listen [::]:80;
    server_name ecosire.com www.ecosire.com;
    return 301 https://ecosire.com$request_uri;
}

التكوين الخاص بـ Cloudflare

خلف Cloudflare، يرى Nginx فقط عناوين IP الخاصة بـ Cloudflare. للحفاظ على عناوين IP الحقيقية للعميل:

# Real IP from Cloudflare — add this in http{} or at top of server{}
set_real_ip_from 103.21.244.0/22;
set_real_ip_from 103.22.200.0/22;
set_real_ip_from 103.31.4.0/22;
set_real_ip_from 104.16.0.0/13;
set_real_ip_from 104.24.0.0/14;
set_real_ip_from 108.162.192.0/18;
set_real_ip_from 131.0.72.0/22;
set_real_ip_from 141.101.64.0/18;
set_real_ip_from 162.158.0.0/15;
set_real_ip_from 172.64.0.0/13;
set_real_ip_from 173.245.48.0/20;
set_real_ip_from 188.114.96.0/20;
set_real_ip_from 190.93.240.0/20;
set_real_ip_from 197.234.240.0/22;
set_real_ip_from 198.41.128.0/17;
set_real_ip_from 2400:cb00::/32;
set_real_ip_from 2606:4700::/32;
set_real_ip_from 2803:f800::/32;
set_real_ip_from 2405:b500::/32;
set_real_ip_from 2405:8100::/32;
set_real_ip_from 2a06:98c0::/29;
set_real_ip_from 2c0f:f248::/32;
real_ip_header CF-Connecting-IP;

أيضًا: أبدا تقسيم www.ecosire.com وecosire.com إلى كتل خادم Nginx منفصلة عند استخدام Cloudflare. يتعامل Cloudflare مع إعادة توجيه www على حافته. إذا قمت بإضافة كتلة خادم www التي تعيد التوجيه إلى موقع غير www، وكان Cloudflare أيضًا يعيد توجيه www، فستحصل على حلقة إعادة التوجيه (ERR_TOO_MANY_REDIRECTS).


تكوين السجل

# Custom log format with useful fields
log_format json_combined escape=json
  '{'
  '"time":"$time_iso8601",'
  '"remote_addr":"$remote_addr",'
  '"cf_ip":"$http_cf_connecting_ip",'
  '"method":"$request_method",'
  '"uri":"$request_uri",'
  '"status":$status,'
  '"body_bytes":$body_bytes_sent,'
  '"response_time":$request_time,'
  '"referrer":"$http_referer",'
  '"user_agent":"$http_user_agent"'
  '}';

access_log /var/log/nginx/ecosire-access.log json_combined;
error_log /var/log/nginx/ecosire-error.log warn;

اختبار التكوين الخاص بك

# Test syntax before applying
nginx -t

# Reload without downtime
nginx -s reload

# Check which config file is active
nginx -T | grep "configuration file"

# Test a specific server block
curl -I https://ecosire.com
curl -I https://api.ecosire.com/api/health

# Check security headers
curl -I https://ecosire.com | grep -E "X-Frame|X-Content|Referrer|Content-Security"

الأسئلة المتداولة

هل يجب أن أستخدم Nginx أو Caddy لمشروع جديد؟

يعد تكوين Caddy أسهل بكثير - فهو يتعامل مع Let's Encrypt SSL تلقائيًا ولديه إعدادات افتراضية معقولة خارج الصندوق. يعتبر Nginx أكثر قوة ويحتوي على نظام بيئي أكبر من الوحدات والوثائق. بالنسبة لمعظم المشاريع الجديدة، تعتبر العلبة هي نقطة البداية الأفضل؛ قم بالتبديل إلى Nginx إذا كنت بحاجة إلى تحكم دقيق في SSL، أو التوجيه المنبع المعقد، أو الوحدات النمطية الخاصة بـ Nginx. بالنسبة لخوادم Linux الحالية التي تم تثبيت Nginx عليها بالفعل، التزم بـ Nginx.

كيف أتجنب الخطأ المكرر Limit_req_zone؟

يجب أن يظهر التوجيه limit_req_zone على مستوى السياق http{}، وليس داخل الكتل server{}. في نشر Turborepo monorepo حيث تقوم بتضمين ملفات تكوين متعددة، تأكد من ظهور التوجيه مرة واحدة فقط عبر جميع الملفات المضمنة. إذا كنت ترى الخطأ، فتحقق مما إذا كان لديك رابط رمزي conf.d/app.conf وsites-enabled/ يشيران إلى نفس الملف.

كيف يمكنني تكوين Nginx لـ Next.js ISR (التجديد الثابت التزايدي)؟

يتعامل Next.js مع ISR داخليًا — يحتاج Nginx فقط إلى وكيل جميع الطلبات إلى Next.js دون تخزين الاستجابات مؤقتًا. لا تقم بإضافة رؤوس Cache-Control التي تتداخل مع رؤوس ذاكرة التخزين المؤقت ISR الخاصة بـ Next.js. بالنسبة للأصول الثابتة في /_next/static/، أضف رؤوس ذاكرة التخزين المؤقت immutable نظرًا لأن هذه الملفات لها أسماء مجزأة للمحتوى. بالنسبة لجميع المسارات الأخرى، اسمح لـ Next.js بتعيين رؤوس ذاكرة التخزين المؤقت.

لماذا تنخفض نقاط SSL الخاصة بي عند تمكين Cloudflare؟

عندما يكون Cloudflare في وضع الوكيل، تقوم SSL Labs باختبار SSL الخاص بـ Cloudflare، وليس الخاص بك. اضبط وضع SSL الخاص بـ Cloudflare على "Full (Strict)" للتأكد من قيام Cloudflare بالتحقق من صحة شهادة الأصل الخاصة بك. قم بتثبيت شهادة Cloudflare Origin على الخادم الخاص بك - فهي مجانية وصالحة لمدة 15 عامًا. تصبح نتيجة SSL Labs الخاصة بك هي نتيجة Cloudflare (عادةً A+)، وهو في الواقع تحسين مقارنة بإعداد Nginx SSL النموذجي الذي تم تكوينه ذاتيًا.

كيف يمكنني التعامل مع تطبيقات Node.js المتعددة على نفس الخادم؟

قم بتشغيل كل تطبيق على منفذ مختلف (Next.js على 3000، NestJS على 3001، Docusaurus على 3002، وما إلى ذلك) وأضف كتلة خادم لكل نطاق فرعي في تكوين Nginx الخاص بك. استخدم PM2 لإدارة جميع عمليات Node.js. يوجه Nginx عبر النطاق الفرعي (أو بادئة المسار) إلى المنفذ الصحيح. يمكن أن يكون لكل كتلة خادم تحديد المعدل الخاص بها والتخزين المؤقت وتكوين رأس الأمان.


الخطوات التالية

يعد تكوين Nginx للإنتاج مستندًا حيًا — فهو يتطور مع تغير متطلبات تطبيقك، وتغير أنماط حركة المرور لديك، وظهور أفضل ممارسات الأمان الجديدة. يقوم ECOSIRE بتشغيل Nginx في الإنتاج لخدمة تطبيقات متعددة، والتعامل مع إنهاء SSL، وتحديد المعدل، وتكامل Cloudflare عبر جميع المجالات.

سواء كنت بحاجة إلى استشارات DevOps، أو إعداد البنية الأساسية للإنتاج، أو تصميم كامل لبنية النشر، استكشف خدماتنا لمعرفة كيف يمكننا مساعدتك.

مشاركة:
E

بقلم

ECOSIRE Research and Development Team

بناء منتجات رقمية بمستوى المؤسسات في ECOSIRE. مشاركة رؤى حول تكاملات Odoo وأتمتة التجارة الإلكترونية وحلول الأعمال المدعومة بالذكاء الاصطناعي.

الدردشة على الواتساب