بیشتر CDNهای امروزی از WebSocket پشتیبانی میکنند، اما معمولاً باید این قابلیت در پنل فعال باشد و سرور اصلی هدرهای Upgrade و Connection را درست پاس بدهد. اتصالهای وبسوکت کش نمیشوند و فقط از CDN عبور میکنند. مهمترین نکتهها: فعالسازی وبسوکت در CDN، تنظیم proxy_http_version 1.1 و هدرهای Upgrade در Nginx، افزایش Timeout و ارسال پیام پینگ دورهای تا اتصال بیکار قطع نشود.
WebSocket چیست و چه فرقی با HTTP معمولی دارد؟
در HTTP معمولی، مرورگر درخواستی میفرستد، سرور پاسخ میدهد و ارتباط تمام میشود. اگر سرور بخواهد خبری را به کاربر برساند، باید منتظر بماند تا مرورگر دوباره درخواست بدهد. برای برنامههایی مثل چت، اعلان زنده، داشبورد لحظهای، بازی آنلاین یا تابلوی قیمت، این مدل کند و پرهزینه است.
WebSocket یک کانال دوطرفه و پایدار بین مرورگر و سرور است. اتصال یک بار برقرار میشود و تا وقتی یکی از دو طرف آن را نبندد باز میماند. در این مدت هم سرور و هم کاربر میتوانند هر لحظه پیام بفرستند. شروع اتصال با یک درخواست HTTP عادی است که هدر Upgrade: websocket دارد؛ اگر سرور موافقت کند، با کد 101 Switching Protocols پاسخ میدهد و از آن لحظه پروتکل عوض میشود.
CDN و WebSocket: مشکل کجاست؟
CDN اساساً برای کش کردن و تحویل پاسخهای کوتاه HTTP طراحی شده است. WebSocket دو ویژگی دارد که با این مدل متفاوت است:
- قابل کش نیست: هر اتصال منحصربهفرد است و پیامها لحظهایاند. CDN فقط باید آن را عبور دهد.
- طولانی است: یک اتصال ممکن است ساعتها باز بماند، در حالی که CDN برای درخواستهای معمولی مهلتهای کوتاهی در نظر میگیرد.
خبر خوب این است که بیشتر CDNهای مدرن، از جمله CDNهای ایرانی و بینالمللی رایج، از WebSocket پشتیبانی میکنند. اما معمولاً باید سه چیز را درست کنید: فعالسازی در پنل CDN، تنظیم سرور اصلی و مدیریت مهلت اتصال. اگر با نقش CDN آشنا نیستید، CDN چیست؟ را بخوانید.
قدم اول: فعالسازی در پنل CDN
در پنل بیشتر CDNها، گزینهای با نام WebSocket در بخش تنظیمات شبکه یا پیشرفته وجود دارد. اگر غیرفعال باشد، CDN هدر Upgrade را حذف میکند و درخواست بهشکل HTTP عادی به سرور میرسد؛ نتیجه معمولاً خطای 400 یا قطع شدن فوری است.
همچنین به پورتهای مجاز دقت کنید. CDNها فقط روی پورتهای مشخصی ترافیک را عبور میدهند؛ معمولاً 80 و 443 و چند پورت دیگر که در مستندات هر CDN فهرست شده است. اگر سرویس وبسوکت شما روی پورت دلخواهی مثل 3001 اجرا میشود، آن را پشت Nginx روی پورت 443 قرار دهید یا مطمئن شوید پورت در فهرست مجاز است.
قدم دوم: تنظیم Nginx روی سرور اصلی
اگر برنامهٔ شما (مثلاً یک سرور Node.js با Socket.IO) پشت Nginx است، Nginx باید هدرهای ارتقا را به برنامه پاس بدهد. Nginx بهطور پیشفرض این کار را نمیکند:
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 443 ssl;
server_name app.example.com;
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 $connection_upgrade;
proxy_set_header Host $host;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}
}
سه خط کلیدیاند: proxy_http_version 1.1 (چون ارتقای پروتکل در HTTP/1.0 وجود ندارد) و دو هدر Upgrade و Connection. بلوک map باید در بخش http قرار گیرد، نه داخل server. پس از تغییر، با nginx -t && systemctl reload nginx اعمال کنید. اصول کلی پراکسی معکوس را در Nginx بهعنوان Reverse Proxy برای داکر و ساختار تنظیمات را در مبانی پیکربندی Nginx ببینید.
- درخواست و پاسخ کوتاه
- سرور نمیتواند خودش پیام بفرستد
- قابل کش در CDN
- مناسب صفحات و API
- اتصال پایدار و دوطرفه
- ارسال لحظهای از هر دو طرف
- هرگز کش نمیشود
- مناسب چت، اعلان و داشبورد زنده
قدم سوم: مهلت اتصال و پیام پینگ
حتی با تنظیم درست، اتصالهای بیکار پس از مدتی قطع میشوند. هم Nginx (با proxy_read_timeout که پیشفرض آن ۶۰ ثانیه است) و هم CDN مهلتی برای اتصال بدون فعالیت دارند. مهلت CDN معمولاً قابل تغییر نیست.
راهحل استاندارد، پیام پینگ دورهای است: سرور یا کلاینت در فاصلههای منظم (کوتاهتر از کمترین مهلت در مسیر) یک پیام کوچک میفرستد تا اتصال فعال بماند. بیشتر کتابخانهها این قابلیت را دارند؛ مثلاً Socket.IO بهطور خودکار ضربان قلب (Heartbeat) میفرستد و در کتابخانهٔ ws در Node.js باید ping را خودتان زمانبندی کنید.
همچنین کلاینت باید اتصال مجدد خودکار داشته باشد. در اینترنت واقعی، اتصالها به دلایل مختلف قطع میشوند: تعویض شبکهٔ گوشی، بهروزرسانی نود CDN یا ریاستارت سرور. برنامهای که پس از قطعی خودش دوباره وصل نشود، برای کاربر خراب به نظر میرسد.
Socket.IO و Long Polling
کتابخانهٔ Socket.IO ابتدا با درخواستهای HTTP معمولی (Long Polling) شروع میکند و سپس به WebSocket ارتقا مییابد. پشت CDN یا چند سرور، این رفتار ممکن است باعث خطای «Session ID unknown» شود، چون درخواستهای پشت سر هم به سرورهای مختلف میرسند. دو راهحل رایج:
- در کلاینت،
transports: ["websocket"]را تنظیم کنید تا مستقیم با WebSocket شروع شود. - اگر چند سرور پشت Load Balancer دارید، چسبندگی نشست (Sticky Session) را فعال کنید.
رفع خطاهای رایج
- خطای 400 Bad Request: هدرهای
UpgradeوConnectionبه برنامه نمیرسند. تنظیمات Nginx و فعال بودن WebSocket در CDN را بررسی کنید. - خطای 502 یا 504: برنامهٔ پشتی در دسترس نیست یا کند پاسخ میدهد. با
curlاز خود سرور به127.0.0.1:3001بررسی کنید. مقالهٔ خطاهای 5xx در CDN هم کمک میکند. - قطع شدن پس از حدود یک دقیقه:
proxy_read_timeoutکوتاه است یا پینگ دورهای ندارید. - کار میکند ولی پشت CDN نه: پورت غیرمجاز است یا گزینهٔ WebSocket در CDN خاموش است.
- Mixed Content: صفحه با HTTPS باز شده ولی آدرس وبسوکت
ws://است. ازwss://استفاده کنید.
برای آزمایش مستقیم اتصال از خط فرمان، ابزار websocat یا این دستور curl کمک میکند:
curl -i -N -H "Connection: Upgrade" -H "Upgrade: websocket" -H "Sec-WebSocket-Version: 13" -H "Sec-WebSocket-Key: SGVsbG8sIHdvcmxkIQ==" https://app.example.com/ws/
اگر پاسخ HTTP/1.1 101 Switching Protocols بود، مسیر تا برنامه درست است.
- فعالسازی در CDN
گزینهٔ WebSocket و پورت مجاز.
- هدرها در Nginx
proxy_http_version 1.1 و Upgrade و Connection.
- افزایش Timeout
proxy_read_timeout برای اتصالهای طولانی.
- پینگ دورهای
پیام کوچک منظم تا اتصال بیکار قطع نشود.
- اتصال مجدد خودکار
کلاینت پس از قطعی دوباره وصل شود.
کی WebSocket را از CDN عبور ندهیم؟
اگر سرویس شما اتصالهای بسیار زیاد و طولانی دارد و به کمترین تأخیر نیاز دارد (مثل سرور بازی)، گاهی بهتر است ترافیک وبسوکت روی یک زیردامنهٔ جداگانه مستقیم به سرور برود و بقیهٔ سایت پشت CDN بماند. البته در این حالت آیپی سرور برای آن زیردامنه آشکار است و محافظت CDN در برابر حملات را از دست میدهید؛ این مصالحه را آگاهانه انتخاب کنید. دربارهٔ اهمیت پنهان ماندن آیپی، حمله DDoS و CDN را ببینید.
در CDN کلودیفای پیش از راهاندازی، پورتها و تنظیمات پشتیبانیشده را در مستندات بررسی کنید و پس از اتصال با دستور curl بالا مسیر را آزمایش کنید.
مقیاسپذیری اتصالهای همزمان
هر اتصال WebSocket باز، روی سرور مقداری حافظه مصرف میکند و یک توصیفگر فایل (File Descriptor) را اشغال میکند. وقتی تعداد کاربران همزمان زیاد میشود، محدودیتهای پیشفرض سیستمعامل و Nginx زودتر از رم یا پردازنده به سقف میرسند. نشانهٔ آن خطاهایی مثل Too many open files در لاگ است.
برای رفع آن، مقدار worker_connections در Nginx را افزایش دهید و سقف فایلهای باز سرویس را بالا ببرید؛ در systemd با گزینهٔ LimitNOFILE در فایل سرویس. نوشتن فایل سرویس را در راهنمای سرویسهای systemd توضیح دادهایم. اگر تعداد اتصالها از توان یک سرور بیشتر شد، چند نمونه از برنامه را پشت Load Balancer اجرا کنید و برای ارسال پیام بین آنها از یک لایهٔ پیامرسان مثل Redis استفاده کنید؛ Socket.IO برای این کار آداپتور آماده دارد.
امنیت اتصالهای وبسوکت
اتصال وبسوکت پس از برقراری، دیگر از سازوکارهای معمول HTTP مثل کوکی در هر درخواست عبور نمیکند. پس احراز هویت را در لحظهٔ شروع اتصال انجام دهید؛ مثلاً با یک توکن کوتاهعمر در پارامتر اتصال. هدر Origin را در سرور بررسی کنید تا سایتهای دیگر نتوانند از مرورگر کاربران شما به سرویس وصل شوند. اندازهٔ پیامها و تعداد پیام در ثانیه را هم محدود کنید تا یک کلاینت مخرب نتواند سرور را از کار بیندازد.
سؤالات متداول
آیا ترافیک WebSocket در CDN کش میشود؟
خیر. اتصال وبسوکت دوطرفه و لحظهای است و CDN فقط آن را بین کاربر و سرور اصلی عبور میدهد. محاسبهٔ ترافیک آن معمولاً مانند ترافیک عادی بر اساس حجم داده است.
چرا اتصال وبسوکت من بعد از یک دقیقه قطع میشود؟
مقدار پیشفرض proxy_read_timeout در Nginx ۶۰ ثانیه است و CDN هم برای اتصال بیکار مهلت دارد. Timeout را در Nginx افزایش دهید و در برنامه پیام پینگ دورهای بفرستید.
wss و ws چه فرقی دارند؟
ws اتصال بدون رمزنگاری و wss اتصال رمزنگاریشده با TLS است، مشابه http و https. در سایتهای HTTPS و پشت CDN همیشه از wss استفاده کنید.
Socket.IO پشت CDN خطای Session ID unknown میدهد؛ چه کنم؟
در کلاینت transports را فقط روی websocket تنظیم کنید یا اگر چند سرور پشت Load Balancer دارید، Sticky Session را فعال کنید تا درخواستهای یک کاربر به یک سرور برسند.
از ۱٬۲۰۰ تومان در ساعت · پرداخت ریالی



