راهنماها

چطور خطاهای CORS را برطرف کنیم (و دلیل وقوعشان)

خطای CORS یعنی صفحه‌ی وب شما تلاش کرده پاسخی را از یک مبدأ دیگر (دامنه، پورت یا پروتکل دیگر) بخواند، و آن سرور هدرهایی نفرستاده که نشان دهد این کار مجاز است. مرورگرها برای محافظت از کاربران، پاسخ را مسدود می‌کنند. شما این خطا را روی سرور برطرف می‌کنید، با برگرداندن هدر Access-Control-Allow-Origin برای مبدأ سایت‌تان و مدیریت درخواست‌های OPTIONS از نوع preflight، یا با فرستادن درخواست از طریق بک‌اِند خودتان روی همان مبدأ از آن دوری می‌کنید.

این یکی از گیج‌کننده‌ترین خطاها در توسعه‌ی وب است، چون درخواست اغلب به سرور می‌رسد. این راهنما توضیح می‌دهد چه اتفاقی می‌افتد و هر نوع خطا را چطور برطرف کنید.

«مبدأ» یعنی چه

مبدأ ترکیب پروتکل، دامنه و پورت است:

نشانیمبدأ
https://myapp.com/dashboardhttps://myapp.com
https://api.myapp.com/usershttps://api.myapp.com (زیردامنه‌ی متفاوت، پس مبدأ متفاوت)
http://localhost:5173http://localhost:5173
http://localhost:3000http://localhost:3000 (پورت متفاوت، پس مبدأ متفاوت)

مرورگرها از سیاست هم‌مبدأ (same-origin) پیروی می‌کنند: جاوااسکریپت یک صفحه آزادانه می‌تواند پاسخ‌های مبدأ خودش را بخواند، ولی پاسخ‌های مبدأهای دیگر را نه. CORS (Cross-Origin Resource Sharing) سازوکاری است که به سرور اجازه می‌دهد داوطلبانه اعلام کند: «پاسخ‌های من را صفحات از این مبداها می‌توانند بخوانند.»

دو نتیجه‌ی غافلگیرکننده:

  • درخواست اغلب به سرور می‌رسد. CORS معمولاً جلوی خواندن پاسخ توسط کد شما را می‌گیرد، نه دریافت درخواست توسط سرور. یک POST ممکن است ذخیره شده باشد با اینکه شما خطا دیده‌اید.
  • فقط مرورگرها آن را اعمال می‌کنند. curl، Postman و کد سمت سرور CORS را کاملاً نادیده می‌گیرند. به همین دلیل «در Postman کار می‌کند» هیچ چیزی درباره‌ی CORS نمی‌گوید.

گام ۱: خطای دقیق را بخوانید

ابزارهای توسعه‌دهنده را باز کنید (راست‌کلیک → Inspect)، به Console بروید و پیام کامل را بخوانید. متن Chrome مشخص می‌کند با کدام مشکل روبه‌رو هستید:

پیام شاملمشکل
No 'Access-Control-Allow-Origin' header is presentسرور اصلاً هدر را نفرستاده، یا درخواست پیش از رسیدن به کد CORS شما شکست خورده (برای مثال یک ۴۰۴ یا ۵۰۰)
The 'Access-Control-Allow-Origin' header has a value '...' that is not equal to the supplied originسرور مبدأ دیگری غیر از مبدأ شما را مجاز کرده است
Response to preflight request doesn't pass access control checkدرخواست preflight از نوع OPTIONS شکست خورده
...must not be the wildcard '*' when the request's credentials mode is 'include'شما کوکی می‌فرستید و سرور * پاسخ داده است
Request header field authorization is not allowedسرور آن هدر را در Access-Control-Allow-Headers فهرست نکرده

سپس تب Network را باز کنید، درخواست ناموفق (و هر درخواست OPTIONS پیش از آن) را پیدا کنید و کد وضعیت و هدرهای پاسخ آن را ببینید.

ابتدا کد وضعیت را بررسی کنید. بسیاری از «خطاهای CORS» در واقع یک خطای سرور هستند که خودش را جای دیگری نشان می‌دهد. اگر سرور کرش کند یا ۴۰۴ برگرداند، پاسخ خطای آن اغلب هدرهای CORS ندارد، پس مرورگر به‌جای مشکل واقعی، CORS گزارش می‌دهد. ۵۰۰ یا ۴۰۴ را اول درست کنید، و ممکن است خطای CORS هم ناپدید شود.

گام ۲: تصمیم بگیرید اصلاً باید از مرورگر این API را صدا بزنید؟

پیش از اضافه کردن هدر، بپرسید: آیا مرورگر باید مستقیم این API را صدا بزند؟

  • یک API شخص ثالث که کلید مخفی می‌خواهد (OpenAI، کلید مخفی Stripe، بیشتر سرویس‌های ایمیل) هرگز نباید از مرورگر صدا زده شود. کلید برای هر بازدیدکننده قابل‌مشاهده می‌شود. آن را از بک‌اِند خودتان صدا بزنید؛ آنگاه مشکل CORS از بین می‌رود، چون درخواست‌های سرور به سرور مشمول CORS نیستند. چطور کلیدهای API را امن نگه داریم را ببینید.
  • یک API شخص ثالث که کنترلش با شما نیست و هدرهای CORS نمی‌فرستد، از سمت شما قابل رفع نیست. آن را از بک‌اِند خودتان صدا بزنید.
  • API خودتان را می‌توان با تنظیم درست رفع کرد. ادامه دهید.

گام ۳: روی سرورتان رفعش کنید

سرور باید هدرهای درست را به پاسخ‌هایش اضافه کند، از جمله پاسخ‌های خطا.

برای درخواست واقعی، این را بفرستید:

  • Access-Control-Allow-Origin: https://myapp.com (مبدأ دقیق فرانت‌اِند شما، بدون اسلش انتهایی)
  • Vary: Origin، اگر مقدار به مبدأ درخواست‌کننده بستگی دارد

برای درخواست preflight از نوع OPTIONS، با یک وضعیت موفق (200 یا 204) و این‌ها پاسخ دهید:

  • Access-Control-Allow-Origin: https://myapp.com
  • Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE
  • Access-Control-Allow-Headers: Content-Type, Authorization (هر هدر سفارشی که می‌فرستید)
  • اختیاری Access-Control-Max-Age: 600، تا مرورگر preflight را برای مدتی کش کند

بیشتر فریم‌ورک‌ها بسته یا تنظیمی دارند که این کار را انجام می‌دهد. در Express بسته‌ی cors؛ در FastAPI، CORSMiddleware؛ در ASP.NET Core، AddCors؛ در Spring، @CrossOrigin یا پیکربندی CORS. یک راه‌اندازی معمول در Express:

import cors from "cors";

app.use(cors({
  origin: ["https://myapp.com", "http://localhost:5173"],
  credentials: true,
}));

مدیریت CORS را پیش از مسیرها و احراز هویت خود قرار دهید، تا درخواست‌های preflight (که ورود ندارند) با ۴۰۱ رد نشوند.

با یک سازنده‌ی هوش مصنوعی، راه‌اندازی را دقیق توصیف کنید:

فرانت‌اِند در https://myapp.com به API در https://api.myapp.com درخواست می‌زند و «Response to preflight request doesn't pass access control check» می‌گیرد. CORS را روی API طوری تنظیم کن که دقیقاً https://myapp.com و مبدأ پیش‌نمایش را مجاز کند، متدهای GET، POST، PATCH و DELETE را با هدرهای Content-Type و Authorization بپذیرد، OPTIONS را پیش از احراز هویت مدیریت کند، و هدرهای CORS را روی پاسخ‌های خطا هم بگذارد.

گام ۴: کوکی‌ها و اعتبارنامه‌ها را مدیریت کنید

اگر فرانت‌اِند کوکی‌ها (مثلاً نشست ورود) را به مبدأ دیگری می‌فرستد، هر دو طرف باید هماهنگ باشند:

  • فرانت‌اِند credentials: "include" را روی fetch می‌گذارد، یا withCredentials: true را در axios.
  • سرور Access-Control-Allow-Credentials: true را می‌فرستد.
  • Access-Control-Allow-Origin سرور باید مبدأ دقیق باشد، نه *.

کوکی‌های بین‌سایتی به SameSite=None; Secure هم نیاز دارند و برخی مرورگرها کوکی‌های شخص ثالث را بیشتر محدود می‌کنند. این یکی دیگر از دلایلی است که رویکرد گام ۵ را ترجیح دهید.

گام ۵: با یک راه‌اندازی هم‌مبدأ از CORS دوری کنید

اغلب تمیزترین راه‌حل این است که اصلاً درخواست بین‌مبدأی نفرستید. API را از همان مبدأ صفحه سرو کنید، برای مثال در https://myapp.com/api/...:

  • در توسعه، بیشتر سرورهای توسعه می‌توانند پروکسی کنند. server.proxy در Vite می‌تواند /api را به بک‌اِند روی پورت دیگری بفرستد، پس مرورگر فقط با یک مبدأ حرف می‌زند.
  • در محیط عملیاتی، /api را از طریق تنظیمات rewrite یا پروکسی میزبانتان به بک‌اِند بفرستید، یا فرانت‌اِند و API را روی یک سرور سرو کنید.

آنگاه فرانت‌اِند یک مسیر نسبی مثل fetch("/api/orders") را صدا می‌زند. دیگر درخواست بین‌مبدأ، preflight بین‌مبدأ و دردسر کوکی وجود ندارد. همچنین نیازی نیست نشانی API را که بین پیش‌نمایش و محیط عملیاتی فرق دارد، سخت‌کد کنید.

فرانت‌اِند را طوری تغییر بده که بک‌اِند را با مسیرهای نسبی /api صدا بزند، نه با یک نشانی کامل، و یک پروکسی توسعه تنظیم کن که /api را به سرور API بفرستد. سپس تنظیمات CORS را که دیگر لازم نیستند حذف کن.

کارهایی که نباید انجام دهید

  • mode: "no-cors" در fetch. چیزی را دور نمی‌زند؛ به کد شما یک پاسخ خالی و غیرقابل‌خواندن می‌دهد.
  • افزونه‌های مرورگر که CORS را خاموش می‌کنند. فقط مرورگر شما را تغییر می‌دهند. هر بازدیدکننده همچنان خطا را می‌بیند.
  • سرویس‌های عمومی «پروکسی CORS» برای اپ‌های واقعی. می‌توانند هر چیزی که از آن‌ها عبور می‌کند را بخوانند و تغییر دهند، و گاهی از کار می‌افتند.
  • Access-Control-Allow-Origin: * روی API ای که ورود دارد. با اعتبارنامه کار نمی‌کند و پیش‌فرض اشتباهی برای داده‌ی خصوصی است.
  • بازتاب دادن هر هدر Origin به پاسخ همراه با مجاز بودن اعتبارنامه. این به هر وب‌سایتی اجازه می‌دهد درخواست‌های ورودیِ کاربران شما را به‌جای آن‌ها بفرستد. از فهرست مجاز استفاده کنید.

علت‌های رایج در اپ‌های ساخته‌شده با هوش مصنوعی

وضعیتراه‌حل
فرانت‌اِند یک API شخص ثالث را با کلید مخفی صدا می‌زندفراخوانی را به بک‌اِند منتقل کنید
در پیش‌نمایش کار می‌کند، روی دامنه‌ی منتشرشده خراب استنشانی‌های منتشرشده و دامنه‌ی اختصاصی را به فهرست مجاز اضافه کنید، یا از مسیرهای نسبی /api استفاده کنید
خطا فقط روی POST، PUT یا DELETEpreflight مدیریت نشده؛ مدیریت OPTIONS و هدرهای مجاز را اضافه کنید
خطا بعد از اضافه کردن ورود ظاهر شداعتبارنامه‌ها به مبدأ دقیق و Allow-Credentials نیاز دارند
«خطای CORS» ولی تب Network کد ۵۰۰ نشان می‌دهدابتدا خطای سرور را برطرف کنید
نشانی API در کد با localhost سخت‌کد شدهاز مسیرهای نسبی یا یک تنظیم جدا برای هر محیط استفاده کنید

CORS در Mythex

وقتی یک پروژه‌ی Mythex یک وب‌سایت و یک سرور API دارد، هر دو به‌عنوان سرویس در همان پروژه اجرا می‌شوند و می‌توانید در پیش‌نمایش بینشان جابه‌جا شوید. از عامل بخواهید که فرانت‌اِند بک‌اِند را از طریق مسیر نسبی /api صدا بزند، که در توسعه پروکسی شود، و بیشتر مشکلات CORS از بین می‌روند. اگر واقعاً به درخواست‌های بین‌مبدأ نیاز دارید، خطای دقیق کنسول و مبداهای درگیر را به عامل بدهید. نتیجه را روی اپ منتشرشده هم بررسی کنید، چون نشانی آن با پیش‌نمایش فرق دارد. معماری چندسرویسی و بک‌اِند API را در مستندات ببینید، و راهنماهای ما درباره‌ی عیب‌یابی یک اپ ساخته‌شده با هوش مصنوعی و فرانت‌اِند در برابر بک‌اِند.

فهرست بررسی CORS

  • خطای کامل کنسول و کد وضعیت درخواست ناموفق را خواندم
  • یک ۴۰۴ یا ۵۰۰ پنهان را رد کردم
  • APIهایی که کلید مخفی دارند از بک‌اِند صدا زده می‌شوند، نه از مرورگر
  • سرور Access-Control-Allow-Origin را با مبداهای مجاز دقیق می‌فرستد
  • درخواست preflight از نوع OPTIONS با متدها و هدرهای مجاز و پیش از احراز هویت پاسخ می‌گیرد
  • هدرهای CORS روی پاسخ‌های خطا هم وجود دارند
  • اعتبارنامه‌ها: مبدأ دقیق، Allow-Credentials: true و تنظیمات درست کوکی
  • مبداهای پیش‌نمایش، منتشرشده و دامنه‌ی اختصاصی همه پوشش داده شده‌اند، یا از مسیرهای نسبی /api استفاده شده
  • از no-cors، پروکسی‌های عمومی و بازتاب هر مبدأ استفاده نشده است

پرسش‌ها

خطای CORS یعنی چه؟

صفحه‌ی شما که از یک مبدأ بارگذاری شده، تلاش کرده پاسخی از مبدأ دیگری را بخواند، و هدرهای پاسخ سرور آن مبدأ اجازه‌ی این کار را نداده است. مرورگر جلوی خواندن پاسخ توسط کد شما را می‌گیرد. راه‌حل روی سرور است یا در نحوه‌ی مسیریابی درخواست، نه در کد فرانت‌اِند.

آیا می‌توانم خطای CORS را از فرانت‌اِند برطرف کنم؟

درست نه. تنظیماتی مثل mode: 'no-cors' فقط پاسخ را از کد شما پنهان می‌کنند. درست آن است که سرور هدر Access-Control-Allow-Origin مناسب را بفرستد، یا API را از بک‌اِند خودتان یا یک پروکسی هم‌مبدأ صدا بزنید.

آیا Access-Control-Allow-Origin: * امن است؟

برای داده‌ی عمومی که هر کسی می‌تواند بخواند، مثل یک API عمومی بدون ورود، اشکالی ندارد. با کوکی یا سایر اعتبارنامه‌ها نمی‌توان از آن استفاده کرد، و برای API های خصوصی باید مبداهای دقیق مجاز را فهرست کنید.

چرا درخواستم در Postman یا curl کار می‌کند ولی در مرورگر نه؟

CORS فقط توسط مرورگرها اعمال می‌شود. ابزارهایی مثل Postman و curl آن را اجرا نمی‌کنند، پس یک درخواست آنجا موفق است ولی وقتی جاوااسکریپت صفحه آن را می‌فرستد، مسدود می‌شود.

درخواست preflight چیست؟

برای درخواست‌هایی که ساده نیستند، مثل POST از نوع JSON یا درخواستی با هدر Authorization، مرورگر ابتدا یک درخواست OPTIONS می‌فرستد و از سرور می‌پرسد کدام متدها و هدرها مجازند. اگر آن درخواست OPTIONS شکست بخورد یا هدرهای درست را نداشته باشد، درخواست اصلی هرگز فرستاده نمی‌شود.

Mythex · Pricing · با هوش مصنوعی هر چیزی بسازید · قالب‌های وب‌سایت و اپلیکیشن · مقایسه‌ی Mythex · واژه‌نامه‌ی ساخت اپلیکیشن با هوش مصنوعی · ابزارهای رایگان ساخت اپلیکیشن با هوش مصنوعی · English

English አማርኛ العربية Български বাংলা Bosanski Català Čeština Dansk Deutsch Ελληνικά Español Español (España) Eesti Suomi Français Français (Canada) ગુજરાતી हिन्दी Hrvatski Magyar Հայերեն Bahasa Indonesia Íslenska Italiano 日本語 ქართული Қазақ тілі ಕನ್ನಡ 한국어 Lietuvių Latviešu Македонски മലയാളം Монгол मराठी Bahasa Melayu မြန်မာ Norsk bokmål Nederlands ਪੰਜਾਬੀ Polski Português (Brasil) Português (Portugal) Română Русский Slovenčina Slovenščina Soomaali Shqip Srpski Svenska Kiswahili தமிழ் తెలుగు ไทย Tagalog Türkçe Українська اردو Tiếng Việt 简体中文 繁體中文(香港) 繁體中文(台灣)