NGINX

پیکربندی Nginx به‌عنوان Reverse Proxy: از server block تا TLS و Rate Limit

کانفیگ تمیز و امن Nginx برای پروکسی‌کردن اپلیکیشن: هدرهای Host و X-Forwarded، WebSocket، HTTPS، محدودیت نرخ و رفع خطاهای رایج 502 و 504.

بیشتر اپلیکیشن‌ها پشت یک reverse proxy بالا می‌آیند: Nginx یک پورت عمومی (۸۰ و ۴۴۳) را می‌گیرد، TLS را خاتمه می‌دهد، درخواست‌ها را به برنامه می‌فرستد و در برابر ترافیک بد سپر می‌شود. این راهنما یک کانفیگ تمیز و امن را قدم‌به‌قدم می‌سازد و می‌گوید هر خط چرا لازم است.

ساده‌ترین reverse proxy

برنامه روی 127.0.0.1:3000 گوش می‌دهد و می‌خواهیم با example.com در دسترس باشد. فایل را در /etc/nginx/conf.d/example.conf (یا sites-available) بگذارید:

server {
    listen 80;
    server_name example.com www.example.com;

    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_set_header کوچک‌اند ولی مهم: بدون Host برنامه نمی‌داند کاربر کدام دامنه را خواسته؛ بدون X-Forwarded-* هر درخواست از 127.0.0.1 و با http به نظر می‌رسد، که لینک‌های بازگشتی و لاگ‌ها را خراب می‌کند. ابزار زیر دقیقاً همین ساختار را از فرم می‌سازد و کانفیگ موجودتان را هم برای نبود این هدرها بررسی می‌کند.

NGXNginx Config Helperserver block بسازید یا کانفیگ موجودتان را پیش از reload بررسی کنید.

HTTPS و ریدایرکت از HTTP

بعد از گرفتن گواهی (مثلاً با Certbot و Let’s Encrypt)، یک server block برای ۸۰ فقط ریدایرکت می‌کند و یکی برای ۴۴۳ کار اصلی را می‌کند:

server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://example.com$request_uri;
}

server {
    listen 443 ssl;
    http2 on;                         # در nginx ≥ 1.25.1؛ در نسخه‌های قدیمی: listen 443 ssl http2;
    server_name example.com;

    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_session_cache shared:SSL:10m;
    ssl_session_timeout 1d;

    add_header Strict-Transport-Security "max-age=31536000" always;

    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;
    }
}
warn ssl_certificate باید به fullchain.pem اشاره کند، نه فقط به گواهی دامنه. اگر زنجیره‌ی میانی ارسال نشود، بعضی کلاینت‌ها (و بسیاری از ابزارهای خط فرمان) خطای «گواهی معتبر نیست» می‌دهند در حالی‌که مرورگر دسکتاپ ممکن است سالم نشان بدهد.

بعد از فعال‌سازی، وضعیت گواهی، تاریخ انقضا و کامل‌بودن زنجیره را بررسی کنید. HSTS را هم بعد از اطمینان از پایداری HTTPS فعال کنید؛ چون مرورگر تا پایان max-age فقط با HTTPS به دامنه وصل می‌شود و برگشت‌دادن آن سخت است.

SSLSSL Certificate Checkerتاریخ انقضا، SAN و زنجیره‌ی گواهی دامنه‌ی خود را بررسی کنید.

WebSocket و اتصال‌های بلندمدت

WebSocket با یک درخواست HTTP Upgrade شروع می‌شود و Nginx به‌طور پیش‌فرض آن هدرها را نمی‌فرستد. اضافه کنید:

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

location /ws/ {
    proxy_pass http://127.0.0.1:3000;
    proxy_http_version 1.1;
    proxy_set_header Upgrade    $http_upgrade;
    proxy_set_header Connection $connection_upgrade;
    proxy_read_timeout 3600s;       # وگرنه اتصال بیکار بعد از ۶۰ ثانیه بسته می‌شود
}

بلوک map باید در سطح http (بیرون از server) باشد؛ فایل‌های داخل conf.d به‌طور پیش‌فرض داخل همان سطح include می‌شوند.

Upstream، keepalive و چند نمونه‌ی برنامه

وقتی چند نمونه از برنامه دارید یا می‌خواهید اتصال‌های به upstream را نگه دارید، یک بلوک upstream تعریف کنید:

upstream app_backend {
    server 127.0.0.1:3000;
    server 127.0.0.1:3001;
    keepalive 32;
}

server {
    # ...
    location / {
        proxy_pass http://app_backend;
        proxy_http_version 1.1;
        proxy_set_header Connection "";   # لازم برای keepalive به upstream
        # ... بقیه‌ی هدرها
    }
}

محدودیت نرخ، حجم و زمان

سه تنظیم کوچک جلوی بخش بزرگی از مشکلات را می‌گیرند:

# در سطح http:
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;

server {
    # ...
    client_max_body_size 10m;          # حداکثر حجم آپلود؛ پیش‌فرض فقط 1m است

    location /api/ {
        limit_req zone=api burst=20 nodelay;
        proxy_pass http://127.0.0.1:3000;
        proxy_connect_timeout 5s;
        proxy_read_timeout 60s;
    }
}
  • limit_req_zone یک «سطل» بر اساس IP می‌سازد؛ rate=10r/s یعنی در حالت پایدار ده درخواست در ثانیه و burst=20 اجازه‌ی اوج کوتاه می‌دهد.
  • خطای 413 Request Entity Too Large تقریباً همیشه از client_max_body_size است.
  • اگر پشت CDN یا لودبالانسر هستید، IP واقعی کاربر در هدر است؛ برای درست‌کارکردن limit_req باید با set_real_ip_from و real_ip_header آن را بازیابی کنید، وگرنه همه‌ی کاربران با یک IP شمرده می‌شوند.

عیب‌یابی 502 و 504

نشانهعلت محتملبررسی
502برنامه خاموش یا پورت اشتباهss -ltnp | grep 3000 و curl -I http://127.0.0.1:3000
502 (Permission denied)دسترسی سوکت یا SELinuxدر RHEL: setsebool -P httpd_can_network_connect 1
504برنامه کند استلاگ برنامه؛ سپس proxy_read_timeout
413آپلود بزرگ‌تر از حدclient_max_body_size
redirect loopبرنامه فکر می‌کند HTTP استهدر X-Forwarded-Proto و تنظیم «trust proxy» برنامه

همیشه قبل از اعمال، پیکربندی را تست کنید و بعد reload بزنید؛ خطای نوشتاری هنگام reload جلوی اعمال را می‌گیرد، ولی در restart ممکن است سرویس را از دسترس خارج کند:

sudo nginx -t && sudo systemctl reload nginx
sudo tail -f /var/log/nginx/error.log

برای دیدن اینکه از دید بیرون چه می‌گذرد (کد وضعیت، ریدایرکت‌ها و هدرها):

HTTPHTTP Headers Viewerکد وضعیت، زنجیره‌ی ریدایرکت و هدرهای امنیتی را بررسی کنید.

نصب Nginx روی سرور ایران

پکیج‌های رسمی Nginx (نسخه‌های جدیدتر از مخزن توزیع) را از صفحه‌ی محصول nginx در dacker.ir می‌توانید با apt یا dnf از یک مخزن داخلی نصب کنید؛ مراحل افزودن کلید و فایل مخزن در راهنمای نصب پکیج‌ها روی سرور ایران آمده است.

جمع‌بندی

یک reverse proxy قابل‌اعتماد با چهار هدر پایه، HTTPS با زنجیره‌ی کامل، تنظیم‌های مناسب برای WebSocket و محدودیت نرخ و حجم ساخته می‌شود، و هر تغییر باید قبل از reload با nginx -t تست شود. برای دیدن وضعیت واقعی سرویس، لاگ‌ها و متریک‌های Nginx را هم مانیتور کنید؛ راه‌اندازی Prometheus و Grafana نقطه‌ی شروع آن است. برای عیب‌یابی مشکلات دسترسی خارج از Nginx، چک‌لیست DNS و SSL را ببینید.

پرسش‌های متداول

خطای 502 Bad Gateway یعنی چه؟
Nginx نتوانسته به upstream وصل شود یا پاسخ نامعتبر گرفته است. معمولاً برنامه‌ی پشت پروکسی خاموش است، پورت یا آدرس اشتباه است، سوکت مجوز دسترسی ندارد یا SELinux/فایروال جلوی اتصال را گرفته. لاگ error.log دلیل دقیق را می‌نویسد.
خطای 504 Gateway Timeout چطور؟
اتصال برقرار شده ولی upstream در مهلت مقرر پاسخ نداده است. اگر پردازش واقعاً طولانی است proxy_read_timeout را بالا ببرید؛ در غیر این صورت علت کندی برنامه را پیدا کنید.
فرق reload و restart؟
reload پیکربندی جدید را بدون قطع اتصال‌های جاری اعمال می‌کند؛ restart فرایند را می‌بندد و دوباره باز می‌کند. در تولید همیشه ابتدا nginx -t و سپس reload.
چرا برنامه‌ی من IP کاربر را 127.0.0.1 می‌بیند؟
چون اتصال از Nginx می‌آید. باید X-Forwarded-For و X-Real-IP را بفرستید و برنامه را طوری تنظیم کنید که فقط از پراکسی‌های مورد اعتماد به آن‌ها اعتماد کند.