گاهی همه‌چیز از نظر فنی درست به نظر میرسه؛ FastAPI بدون مشکل اجرا شده، Endpoint جواب میده، آدرس هم درسته و حتی وقتی همون API رو با ابزار تست باز میکنید پاسخ مورد انتظار رو میگیرید، اما به محض اینکه فرانت‌اند از داخل مرورگر درخواست میفرسته، همه‌چیز متوقف میشه و مرورگر خطایی درباره CORS نشون میده. اینجا معمولاً مشکل از منطق Endpoint نیست، بلکه مرورگر داره یه قانون امنیتی رو اجرا میکنه که مشخص میکنه یه سایت تا چه حد اجازه داره به یه آدرس دیگه درخواست بفرسته.

به این قانون مربوط به اجازه دسترسی بین دو مبدأ متفاوت اصطلاحاً CORS گفته میشه و اگه فرانت‌اند و API روی دامنه، پروتکل یا پورت متفاوتی اجرا بشن، باید این دسترسی به شکل مشخص روی سرور تنظیم بشه.

CORS در FastAPI چیست؟

مرورگر هر سایت رو بر اساس سه چیز تشخیص میده: پروتکل، دامنه و پورت. ترکیب این سه مورد چیزی رو میسازه که بهش مبدأ درخواست گفته میشه و اصطلاح فنی اون Origin هست. بنابراین حتی دو آدرسی مثل http://localhost:3000 و http://localhost:8000 از نظر مرورگر یکسان نیستن، چون پورتشون فرق داره.

این موضوع توی توسعه خیلی رایجه، چون ممکنه React، Vue یا هر فرانت‌اند دیگه‌ای روی یه پورت اجرا بشه و FastAPI روی پورت دیگه. وقتی مرورگر میبینه یه صفحه از Origin اول میخواد به Origin دوم درخواست بفرسته، بررسی میکنه که سرور مقصد اصلاً چنین دسترسی‌ای رو مجاز کرده یا نه. اگه پاسخ مناسب وجود نداشته باشه، مرورگر ممکنه جلوی دسترسی فرانت‌اند به پاسخ API رو بگیره.

چرا API سالم است ولی مرورگر خطا می‌دهد؟

این قسمت یکی از چیزهاییه که خیلی از مبتدی‌ها رو گیج میکنه، چون ممکنه همون Endpoint توی Postman یا Swagger کاملاً سالم جواب بده ولی داخل مرورگر خطای CORS ببینید. دلیلش اینه که CORS بیشتر یه سیاست امنیتی در سطح مرورگره و ابزارهای تست API الزاماً همون محدودیت رو به همون شکل اعمال نمیکنن.

پس وقتی این خطا رو میبینید، نباید سریع نتیجه بگیرید که FastAPI خراب شده یا Route درست کار نمیکنه. خیلی وقت‌ها خود API سالمه و فقط مرورگر اجازه نداره پاسخ اون رو در اختیار فرانت‌اند فعلی قرار بده.

تنظیم CORS در FastAPI با CORSMiddleware

FastAPI برای مدیریت این موضوع یه لایه آماده داره که قبل از رسیدن Request به Endpoint و هنگام ساخت Response میتونه Headerهای مربوط به دسترسی رو مدیریت کنه. به چنین لایه‌ای میان‌افزار گفته میشه و اصطلاح انگلیسی اون Middleware هست. ابزار مخصوص CORS در FastAPI هم با اسم CORSMiddleware شناخته میشه.

یه تنظیم ساده میتونه این شکلی باشه:

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

app = FastAPI()

origins = [
    "http://localhost:3000",
    "http://127.0.0.1:3000"
]

app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"]
)

در این مثال مشخص کردیم فقط درخواست‌هایی که از Originهای تعریف‌شده میان اجازه دسترسی داشته باشن. allow_methods مشخص میکنه چه متدهایی مثل GET یا POST مجاز باشن و allow_headers هم تعیین میکنه چه Headerهایی اجازه ارسال داشته باشن. اگه پروژه از Cookie یا اطلاعات ورود استفاده میکنه، allow_credentials=True هم اهمیت پیدا میکنه.

allow_origins را دقیق تنظیم کنید

مهم‌ترین بخش این تنظیمات allow_origins هست، چون مشخص میکنه چه سایت‌هایی اجازه دارن از داخل مرورگر به API شما دسترسی داشته باشن. توی محیط توسعه ممکنه آدرس فرانت‌اند http://localhost:3000 باشه، ولی روی سرور واقعی احتمالاً چیزی مثل https://app.example.com دارید و بهتره همون دامنه واقعی داخل لیست قرار بگیره.

گاهی برای راحتی از ["*"] استفاده میشه که یعنی همه Originها مجاز باشن، ولی این انتخاب برای پروژه واقعی همیشه مناسب نیست، مخصوصاً وقتی بحث Cookie، اطلاعات ورود یا Endpointهای حساس وسط باشه. تنظیم دقیق Originها باعث میشه فقط سرویس‌هایی که واقعاً به API نیاز دارن اجازه دسترسی داشته باشن و تنظیمات پروژه هم قابل‌کنترل‌تر بمونه.

تفاوت تنظیم CORS در Development و Production

در محیط توسعه معمولاً چند سرویس روی localhost و پورت‌های مختلف اجرا میشن، بنابراین داشتن چند Origin محلی کاملاً طبیعیه. اما وقتی پروژه وارد محیط واقعی میشه، بهتره لیست Originها محدود به دامنه‌های واقعی پروژه باشه و تنظیمات آزمایشی مثل localhost بدون دلیل وارد Production نشن.

این جداسازی فقط برای مرتب بودن تنظیمات نیست؛ هرچی دامنه‌های مجاز دقیق‌تر تعریف بشن، رفتار API هم قابل پیش‌بینی‌تر میشه و احتمال اینکه یه Origin ناخواسته به درخواست‌ها دسترسی داشته باشه کمتر میشه.

Preflight Request چیست؟

برای بعضی Requestها مرورگر قبل از ارسال درخواست اصلی، اول یه درخواست مقدماتی میفرسته تا از سرور بپرسه آیا Origin فعلی، نوع متد و Headerهای موردنظر مجاز هستن یا نه. به این مرحله درخواست پیش‌بررسی گفته میشه و اصطلاح فنی اون Preflight Request هست.

این درخواست معمولاً با متد OPTIONS ارسال میشه و اگه CORSMiddleware درست تنظیم شده باشه، FastAPI پاسخ لازم رو میده و بعد مرورگر اجازه میده درخواست اصلی ارسال بشه. بنابراین وقتی یه Request ساده GET کار میکنه ولی یه POST با Header خاص Fail میشه، یکی از چیزهایی که باید بررسی کنید همین مرحله Preflight هست.

هر خطای شبیه CORS واقعاً از CORS نیست

یه اشتباه رایج اینه که با دیدن هر پیام مرتبط با CORS، سریع Originها رو بازتر کنیم، در حالی که ممکنه مشکل اصلی چیز دیگه‌ای باشه. مثلاً سرور ممکنه Crash کرده باشه، Reverse Proxy پاسخ ناقص برگردونه، HTTPS و HTTP با هم قاطی شده باشن یا درخواست اصلاً به FastAPI نرسیده باشه.

برای همین قبل از دستکاری تنظیمات، باید مطمئن بشید خود API در دسترسه، آدرس و پورت درست نوشته شده، Origin دقیقاً با مقدار داخل تنظیمات یکیه و سرور Response معتبر برمیگردونه. باز کردن بیش از حد CORS بدون پیدا کردن علت واقعی معمولاً فقط صورت مسئله رو پاک میکنه.

CORS جای Authentication را نمی‌گیرد

این نکته خیلی مهمه که CORS رو با سیستم امنیت کاربر اشتباه نگیریم. CORS فقط مشخص میکنه چه Originهایی از داخل مرورگر اجازه ارتباط با API رو دارن، ولی تعیین نمیکنه چه کاربری اجازه مشاهده یا تغییر داده رو داره.

برای تشخیص هویت کاربر و کنترل دسترسی همچنان باید از Token، Session یا روش مناسب احراز هویت استفاده کنید. بنابراین حتی اگه فقط دامنه فرانت‌اند خودتون در CORS مجاز باشه، Endpointهای حساس همچنان باید Authentication و Authorization درست داشته باشن.

جمع‌بندی

خطای CORS در FastAPI معمولاً زمانی دیده میشه که فرانت‌اند و API روی Originهای متفاوت اجرا میشن و مرورگر هنوز اجازه ارتباط بین اون‌ها رو پیدا نکرده. با CORSMiddleware میشه مشخص کرد چه Originهایی مجاز هستن، چه متدها و Headerهایی اجازه دارن و آیا اطلاعات مربوط به ورود کاربر میتونه همراه Request ارسال بشه یا نه.

تو محیط توسعه تنظیمات میتونه کمی بازتر باشه، ولی در Production بهتره فقط دامنه‌های واقعی پروژه مجاز باشن. همچنین اگه API توی ابزار تست جواب میده ولی داخل مرورگر بلاک میشه، CORS یکی از اولین چیزهاییه که باید بررسی کنید، ولی قبل از تغییر تنظیمات باید مطمئن بشید مشکل واقعاً از همین بخشه.