Skip to Content

تنظیم Nginx به‌عنوان Reverse Proxy برای Docker؛ شبکه، Header، WebSocket و عیب‌یابی

برای اتصال Nginx به سرویس Docker، شبکه مشترک، نام سرویس، Host و Forwarded Header، WebSocket، timeout، سلامت upstream و مرز اعتماد IP را درست تنظیم کنید.

نویسنده مدیر کل تاریخ انتشار
این پست را به اشتراک بگذارید

وقتی برنامه داخل container اجرا می‌شود، بهتر است فقط Nginx پورت عمومی را بگیرد و درخواست‌ها را از network داخلی به سرویس برنامه بفرستد. اما یک proxy_pass تنها کافی نیست: آدرس localhost از داخل کانتینر Nginx به خود Nginx اشاره می‌کند، نه برنامه؛ و اگر headerهای مبدا درست منتقل نشوند، لینک‌های HTTPS، IP کاربر یا WebSocket ممکن است خراب شوند.

پاسخ سریع: Nginx و app را به network مشترک وصل کنید، upstream را با نام سرویس و پورت داخلی هدف بگیرید، فقط 80/443 را publish کنید و Host و scheme اصلی را منتقل کنید. timeout، WebSocket و محدودیت upload را با نیاز برنامه تنظیم کنید. سپس از بیرون TLS و HTTP، و از داخل network اتصال upstream را آزمون کنید.

مدل شبکه را قبل از config بکشید

جریان معمول چنین است: مرورگر → DNS → فایروال/CDN → Nginx → سرویس app → dependency داخلی. Nginx باید روی networkی باشد که app را می‌بیند؛ دیتابیس نیاز ندارد به network عمومی یا proxy وصل شود. اگر Nginx روی host نصب شده و app در Compose است، مسیر اتصال متفاوت است و باید پورت app فقط روی loopback host منتشر شود. این دو حالت را در یک config مبهم مخلوط نکنید.

نام سرویس به‌جای IP کانتینر

در network Compose، سرویس‌ها معمولاً با نام سرویس یکدیگر را پیدا می‌کنند. IP کانتینر پس از recreate ممکن است تغییر کند و نباید در فایل Nginx ثابت نوشته شود. Nginx نیز بسته به شکل config ممکن است نام upstream را هنگام startup resolve و بعد همان IP را نگه دارد؛ پس deploy جدید را با reload مناسب proxy یا الگوی resolution پویا آزمون کنید. خطای 502 بلافاصله پس از recreate نشانه مهمی برای این مسیر است.

نمونه حداقلی برای یک backend HTTP

server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://app:8000;
        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;
    }
}

این نمونه فقط زمانی کار می‌کند که سرویس Compose به نام app روی همان network و پورت داخلی 8000 در حال گوش‌دادن باشد. دامنه و پورت را با پروژه عوض کنید. برای production اینترنتی باید TLS و redirect صحیح نیز اضافه شود؛ HTTP این مثال را نسخه آماده استقرار تلقی نکنید. راهنمای SSL سرویس‌های Docker گواهی و تمدید را جدا بررسی می‌کند.

چرا Host مهم است؟

بسیاری برنامه‌ها دامنه را برای ساخت URL مطلق، چندسایتی یا کنترل CSRF استفاده می‌کنند. Nginx به‌صورت پیش‌فرض الزاماً همان Host اولیه را به upstream نمی‌دهد؛ در config باید رفتار مطلوب را صریح کنید. هم‌زمان برنامه باید hostnameهای مجاز را کنترل کند تا header دلخواه کاربر به لینک مخرب یا redirect ناخواسته تبدیل نشود. ارسال header به‌تنهایی اعتبارسنجی سمت برنامه نیست.

IP واقعی و زنجیره Proxy

X-Forwarded-For ممکن است از سمت client یا CDN از قبل مقدار داشته باشد؛ نباید آن را بدون مرز اعتماد، IP قطعی کاربر دانست. اگر پشت CDN یا load balancer هستید، Nginx و برنامه باید فقط proxyهای قابل‌اعتماد را به‌عنوان منبع header بپذیرند. در غیر این صورت log، rate limit و کنترل دسترسی مبتنی بر IP قابل‌دورزدن می‌شود. IP واقعی را با درخواست آزمایشی و log هر لایه راستی‌آزمایی کنید.

Scheme و redirect loop

اگر TLS روی Nginx terminate می‌شود ولی app فقط HTTP داخلی می‌بیند، app ممکن است درخواست را ناامن تصور کند و دائماً به HTTPS redirect دهد یا URL اشتباه بسازد. X-Forwarded-Proto باید scheme مرورگر را برساند و app فقط از proxy مورداعتماد آن را بپذیرد. اگر TLS قبل از Nginx در CDN تمام می‌شود، $scheme در Nginx ممکن است http باشد؛ در این حالت chain اعتماد و policy اتصال CDN به origin را جدا طراحی کنید.

WebSocket و ارتباط‌های طولانی

WebSocket به تنظیم درست headerهای Upgrade/Connection و نسخه HTTP مناسب در hop proxy نیاز دارد. برای SSE و streaming نیز buffering و timeout را با رفتار app هماهنگ کنید. کپی‌کردن یک config عمومی WebSocket برای همه routeها لازم نیست؛ فقط endpoint مربوط را هدف بگیرید. timeout بسیار بلند بدون محدودیت اتصال می‌تواند ظرفیت worker را مصرف کند؛ timeout کوتاه نیز session واقعی را قطع می‌کند.

Timeout و اندازه upload

خطای 504 همیشه از Nginx نیست؛ ممکن است app، دیتابیس یا dependency کند باشد. proxy_connect_timeout، proxy_read_timeout و proxy_send_timeout اهداف متفاوت دارند. آن‌ها را برای پنهان‌کردن query کند بی‌حساب بالا نبرید. محدودیت client_max_body_size باید با نیاز upload و محدودیت برنامه هماهنگ باشد؛ تغییر آن بدون کنترل نوع فایل و فضای ذخیره‌سازی خطر ایجاد می‌کند.

Static file و cache

اگر Nginx فایل static را مستقیم سرو می‌کند، mount فقط‌خواندنی و مسیر دقیق لازم است. cache headerها را برای فایل‌های دارای نام نسخه‌دار می‌توان بلندتر گذاشت، اما HTML یا پاسخ شخصی‌سازی‌شده نباید با همان سیاست cache شود. compression و cache باید با رفتار CDN و framework آزموده شود؛ بهینه‌سازی عجولانه می‌تواند نسخه قدیمی asset یا محتوای کاربر دیگر را نشان دهد.

Health و ترتیب راه‌اندازی

running بودن container app مساوی ready بودن آن نیست. healthcheck endpoint کم‌هزینه، retry کنترل‌شده در برنامه و check بیرونی HTTP لازم است. اگر Nginx پیش از app شروع شود، ممکن است خطای موقت قابل‌انتظار ببینید؛ ولی خطای ماندگار نیاز به بررسی DNS، network، listener و log دارد. مانیتورینگ سرور به لایه‌های فراتر از process می‌پردازد.

فرایند عیب‌یابی 502

  1. نام سرویس و پورت داخلی را با config واقعی app تطبیق دهید.
  2. مطمئن شوید Nginx و app روی یک network هستند.
  3. از داخل container proxy، resolve نام و اتصال به پورت app را بررسی کنید.
  4. log Nginx و app را در یک بازه زمانی مقایسه کنید.
  5. پس از recreate، IP قبلی cacheشده در Nginx را بررسی و reload امن انجام دهید.
  6. اگر اتصال برقرار است، startup/health و timeout dependency را بررسی کنید.

به‌جای بازکردن پورت app روی اینترنت برای «حل سریع»، مسیر داخلی را اصلاح کنید. برای تشخیص از ابزار خواندنی و درخواست آزمایشی استفاده کنید؛ تغییر firewall یا شبکه بدون برنامه برگشت می‌تواند سرویس را بیشتر قطع کند.

اعتبارسنجی قبل از reload

پیش از اعمال config، syntax و نام upstream را در همان محیط اجرا بررسی کنید. reload باید فقط پس از موفقیت آزمون انجام شود و یک درخواست واقعی با Host/TLS درست پاسخ بگیرد. اگر چند virtual host دارید، مطمئن شوید default server درخواست دامنه ناشناخته را به برنامه حساس نمی‌فرستد. نسخه قبلی config را برای rollback نگه دارید.

یک آزمون درخواست از ابتدا تا انتها

برای یک مسیر کم‌خطر، وضعیت HTTP، زمان پاسخ، hostname و scheme نهایی را از بیرون ثبت کنید. سپس در log Nginx همان request را پیدا کنید و ببینید upstream status و upstream response time چه بوده است. در log برنامه نیز request ID یا زمان متناظر را بررسی کنید. اگر Nginx 200 می‌دهد اما محتوای دامنه دیگری نمایش داده می‌شود، server_name، Host header و تنظیم trusted host برنامه را بررسی کنید. اگر فقط درخواست‌های طولانی شکست می‌خورند، ابتدا مدت پردازش app و dependency را اندازه بگیرید؛ تغییر timeout آخرین مرحله تشخیص است، نه اولین واکنش. در این آزمون داده پرداخت یا رمز واقعی در URL و log قرار ندهید.

مرز مسئولیت Proxy و برنامه

Proxy می‌تواند TLS، محدودیت اندازه درخواست و بعضی سیاست‌های نرخ را اعمال کند؛ اعتبارسنجی کاربر، مجوز دسترسی و صحت داده همچنان مسئولیت برنامه است. اگر proxy را دور بزنند و app مستقیم publish شده باشد، کنترل‌های لبه بی‌اثر می‌شوند. بازبینی پورت‌های host و ruleهای firewall را بخشی از آزمون امنیتی قرار دهید.

اشتباه‌های رایج

  • نوشتن localhost به‌جای نام سرویس
  • استفاده از IP ثابت container
  • publish کردن دیتابیس به host
  • اعتماد بی‌قید به X-Forwarded-For
  • نبود تنظیم WebSocket برای route لازم
  • بالابردن timeout برای پوشاندن app کند
  • reload نکردن Nginx پس از تغییر IP upstream در config ایستا

چه زمانی کمک تخصصی لازم است؟

اگر چند دامنه، CDN، WebSocket، TLS و چند سرویس پشت یک proxy دارید، خطاهای کوچک در header یا شبکه می‌تواند login، پرداخت و محدودسازی IP را مختل کند. داکرایز اپلیکیشن می‌تواند طراحی شبکه، reverse proxy، health و rollout را در staging آزمون و برای production مستند کند.

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

آیا باید پورت app را روی host منتشر کنم؟

اگر Nginx داخل network مشترک است، معمولاً نه. اگر Nginx روی host است، publish محدود به loopback می‌تواند لازم باشد.

چرا بعد از deploy جدید 502 می‌گیرم؟

listener، health یا resolve قدیمی upstream در Nginx را بررسی کنید؛ IP container را ثابت فرض نکنید.

آیا X-Forwarded-For همیشه قابل‌اعتماد است؟

خیر؛ فقط header رسیده از proxyهای تعریف‌شده و مورداعتماد باید مبنای IP واقعی شود.

آیا TLS بین Nginx و app هم لازم است؟

به مرز شبکه و مدل تهدید بستگی دارد؛ روی network مشترک یک host ممکن است HTTP داخلی پذیرفته باشد، ولی بین hostها باید محافظت جداگانه ارزیابی شود.

تفاوت Docker Compose در Development و Production؛ چه چیزهایی باید واقعاً عوض شوند؟
در توسعه سرعت تغییر کد مهم است؛ در production پایداری، image ثابت، secret، شبکه محدود، backup و rollback. تفاوت فایل‌های Compose را با چک‌لیست ببینید.