اگه با API کار کرده باشی، حتماً با این ارور اعصابخُردکن برخورد کردی:
پیغام Access to fetch has been blocked by CORS policy (مسدود شدن درخواست بهخاطر سیاست CORS)
آدرس درسته، کدت سالمه، اما مرورگر اجازه نمیده. نکته مهم اینه: CORS خطا نیست؛ یه مکانیزم امنیتیه که درست داره کارش رو انجام میده.
بریم ببینیم داستان چیه
اول اصل ماجرا: قانون SOP
مرورگر یه قانون پایه داره به اسم Same-Origin Policy یا SOP (سیاست هممبدأ). طبق این قانون، یه سایت فقط اجازه داره به پاسخِ درخواستهایی دسترسی داشته باشه که از همون مبدأ اومدن.
پس CORS دقیقاً چیکار میکنه؟
حالا CORS (اشتراکگذاری منابع بین مبدأهای مختلف) راهیه که سرور میتونه بهصورت کنترلشده به مرورگر بگه: «اوکیه، اجازه بده این مبدأ خاص به پاسخ من دسترسی داشته باشه.»
پس CORS دشمن SOP نیست. یه جورایی مکمل هم دیگه هستن
چرا اصلاً این محدودیت لازمه؟ (سناریوی بانک)
فرض کن توی یه تب وارد حساب بانک شدی و لاگین هستی. کوکیها (اطلاعات نشست کاربر) ذخیره شدن.
حالا توی یه تب دیگه، یه سایت مخرب باز میکنی و این کد اجرا میشه:
fetch("https://bank.com/api/balance")مرورگر ممکنه درخواست رو با کوکیهای تو بفرسته، اما نکته حیاتی اینجاست:
بدون CORS، سایت مخرب نباید بتونه پاسخ سرور رو بخونه.
اینجا SOP و CORS وارد میشن و میگن: «درخواست شاید بره، ولی دسترسی به پاسخ ممنوعه.»
نکته: جمله دسترسی به پاسخ ممنوعه برای متدهای GET درسته اما برای متدهای تغییردهنده مثل POST یا DELETE که اثر جانبی (Side Effect) دارن، خطر اصلی ارسال خودِ درخواست وجود داره (نه فقط صرف دسترسی به پاسخ). برای همین Preflight Request درست شد تا از رسیدنِ درخواستهای غیرایمن به سرور جلوگیری کنن، نه اینکه فقط جلوی خوندن پاسخ رو بگیره. که در ادامه بیشتر راجبش توضیح دادم.
نتیجه: اطلاعات حساس کاربر لو نمیره.
مبدأ یا Origin فقط دامین نیست. مرورگر سه چیز رو با هم چک میکنه:
پروتکل (http یا https)
هاست (نام دامنه مثل site.com)
پورت (مثل 80، 443، 5173)
اگه یکی فرق کنه، Origin فرق کرده:
❌ https://site.dev vs https://api.site.dev (هاست متفاوت)
❌ https://site.dev vs http://site.dev (پروتکل متفاوت)
❌ localhost:3000 vs localhost:8000 (پورت متفاوت)
چون CORS فقط توسط مرورگر enforce میشه (اعمال میشه).
حالا Postman و curl مشمول SOP نیستن. مهم نیست کوکی دارن یا نه؛ اصلاً این قوانین براشون وجود نداره.
مرورگر اما جاییه که کدِ هزاران سایت مختلف کنار هم اجرا میشه، پس مجبورِ سختگیر باشه.
برای درخواستهای حساس (مثل PUT، DELETE یا ارسال JSON)، مرورگر ریسک نمیکنه.
اول یه درخواست OPTIONS (درخواست آزمایشی) میفرسته و از سرور میپرسه:
«اجازه هست با این متد و این هدرها درخواست بدم؟»
اگه سرور با هدرهای مناسب جواب بده، درخواست اصلی ارسال میشه.
این فقط برای non-simple request (درخواستهای غیرساده) اتفاق میافته؛ GETهای ساده معمولاً preflight ندارن.
قاعده طلایی:
مسئله CORS از فرانتاند (کد جاوااسکریپت سمت کاربر) قابل حل نیست.
سروره که باید صریحاً بگه به کدوم Origin اجازه دسترسی میده:
Access-Control-Allow-Origin: https://my-app.com (دامنه مجاز)⚠️ هشدار مهم: Wildcard (*) راهحل نیست
یکی از رایجترین اشتباهها برای «حل» CORS اینه که روی سرور بنویسن:
Access-Control-Allow-Origin: *
این کار فقط در سادهترین حالتها بیخطره. به محض اینکه Credentials (کوکی، سشن، Authorization header) وارد بازی بشن، wildcard عملاً امنیت رو نابود میکنه.
مرورگر حتی طبق استاندارد اجازه نمیده * با Access-Control-Allow-Credentials: true همزمان استفاده بشه، چون یعنی: «هر سایتی به اطلاعات احراز هویتشدهی کاربر دسترسی داشته باشه».
راه درست اینه که Originها رو صریح و محدود تعریف کنی، نه اینکه در رو به روی همه باز بذاری و CORS اگر بد تنظیم بشه، دیگه مکانیزم امنیتی نیست میشه توهم امنیت.
در محیط توسعه، ابزارهایی مثل dev proxy فقط درخواست رو server-side (سمت سرور) میکنن و CORS رو دور نمیزنن، فقط مسیر درخواست رو عوض میکنن.
دفعه بعد که ارور CORS دیدی، عصبی نشو.
این مرورگره که داره از اطلاعات کاربر محافظت میکنه.
راهحل همیشه طرفه سرور.
—-
🆔 @MdDaily



