Ghiduri

Cum repari erorile CORS (și de ce apar)

O eroare CORS înseamnă că pagina ta web a încercat să citească un răspuns de la o altă origine (alt domeniu, port sau protocol), iar acel server nu a trimis header-e care să spună că este permis. Browserele blochează răspunsul ca să protejeze utilizatorii. O repari pe server, returnând un header Access-Control-Allow-Origin pentru originea site-ului tău și tratând cererile preflight OPTIONS, sau o eviți trimițând cererea prin propriul backend, pe aceeași origine.

Este una dintre cele mai confuze erori din dezvoltarea web, pentru că adesea cererea ajunge la server fără probleme. Acest ghid explică ce se întâmplă și cum se repară fiecare variantă.

Ce înseamnă „origine”

O origine este combinația dintre protocol, domeniu și port:

URLOrigine
https://aplicatia-mea.ro/dashboardhttps://aplicatia-mea.ro
https://api.aplicatia-mea.ro/usershttps://api.aplicatia-mea.ro (alt subdomeniu, deci altă origine)
http://localhost:5173http://localhost:5173
http://localhost:3000http://localhost:3000 (alt port, deci altă origine)

Browserele urmează same-origin policy: JavaScript-ul unei pagini poate citi liber răspunsurile de la propria origine, dar nu de la altele. CORS (Cross-Origin Resource Sharing) este sistemul prin care un server își dă acordul: „răspunsurile mele pot fi citite de pagini din aceste origini.”

Două consecințe surprind pe mulți:

  • Cererea ajunge adesea la server. CORS de obicei blochează codul tău să citească răspunsul, nu serverul să primească cererea. Un POST poate fi salvat chiar dacă ai văzut o eroare.
  • Doar browserele aplică regula. curl, Postman și codul de pe server ignoră complet CORS. De aceea „funcționează în Postman” nu spune nimic despre CORS.

Pasul 1: Citește eroarea exactă

Deschide uneltele de dezvoltator (click dreapta → Inspect), mergi la tab-ul Console și citește mesajul complet. Formularea Chrome îți spune ce problemă ai:

Mesajul conțineCe nu este în regulă
No 'Access-Control-Allow-Origin' header is presentServerul nu a trimis deloc header-ul sau cererea a eșuat înainte să ajungă la codul tău CORS (de exemplu un 404 sau un 500)
The 'Access-Control-Allow-Origin' header has a value '...' that is not equal to the supplied originServerul permite o altă origine decât a ta
Response to preflight request doesn't pass access control checkPreflight-ul OPTIONS a eșuat
...must not be the wildcard '*' when the request's credentials mode is 'include'Trimiți cookie-uri, iar serverul a răspuns cu *
Request header field authorization is not allowedServerul nu a listat header-ul respectiv în Access-Control-Allow-Headers

Apoi deschide tab-ul Network, găsește cererea care a eșuat (și orice cerere OPTIONS dinaintea ei) și uită-te la codul de status și la header-ele de răspuns.

Verifică întâi codul de status. Multe „erori CORS” sunt de fapt o eroare de server în deghizare. Dacă serverul se blochează sau returnează 404, răspunsul de eroare nu are adesea header-ele CORS, deci browserul raportează CORS în loc de problema reală. Repară eroarea 500 sau 404, și eroarea CORS poate dispărea.

Pasul 2: Decide dacă ar trebui să apelezi API-ul acela din browser

Înainte să adaugi header-e, întreabă-te: ar trebui ca browserul să apeleze direct acest API?

  • Un API terț care cere o cheie secretă (OpenAI, cheia secretă Stripe, majoritatea serviciilor de e-mail) nu ar trebui apelat niciodată din browser. Cheia ar fi vizibilă pentru fiecare vizitator. Apelează-o din backend-ul tău, iar problema CORS dispare, pentru că cererile server-la-server nu sunt supuse CORS. Vezi cum păstrezi în siguranță cheile API.
  • Un API terț pe care nu îl controlezi, care nu trimite header-e CORS, nu poate fi reparat din partea ta. Apelează-o din backend-ul tău.
  • API-ul tău poate fi reparat configurându-l corect. Continuă mai jos.

Pasul 3: Repară pe serverul tău

Serverul trebuie să adauge header-ele corecte la răspunsurile sale, inclusiv la răspunsurile de eroare.

Pentru cererea efectivă, trimite:

  • Access-Control-Allow-Origin: https://aplicatia-mea.ro (originea exactă a frontend-ului tău, fără slash la final)
  • Vary: Origin, dacă valoarea depinde de originea care a cerut

Pentru cererea preflight OPTIONS, răspunde cu un status de succes (200 sau 204) și cu:

  • Access-Control-Allow-Origin: https://aplicatia-mea.ro
  • Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE
  • Access-Control-Allow-Headers: Content-Type, Authorization (fiecare header personalizat pe care îl trimiți)
  • Opțional Access-Control-Max-Age: 600, ca browserul să păstreze în cache preflight-ul un timp

Majoritatea framework-urilor au un pachet sau o setare care face asta. În Express, pachetul cors; în FastAPI, CORSMiddleware; în ASP.NET Core, AddCors; în Spring, @CrossOrigin sau o configurare CORS. Un exemplu tipic în Express:

import cors from "cors";

app.use(cors({
  origin: ["https://aplicatia-mea.ro", "http://localhost:5173"],
  credentials: true,
}));

Pune gestionarea CORS înainte de rutele și de autentificarea ta, ca cererile preflight (care nu transportă autentificare) să nu fie respinse cu 401.

Cu un constructor cu AI, descrie setarea precis:

Frontend-ul de la https://aplicatia-mea.ro apelează API-ul de la https://api.aplicatia-mea.ro și primește „Response to preflight request doesn't pass access control check”. Configurează CORS pe API ca să permită exact https://aplicatia-mea.ro și originea de previzualizare, permite GET, POST, PATCH, DELETE cu header-ele Content-Type și Authorization, tratează OPTIONS înainte de autentificare și include header-ele CORS și în răspunsurile de eroare.

Pasul 4: Tratează cookie-urile și credențialele

Dacă frontend-ul tău trimite cookie-uri (de exemplu o sesiune de autentificare) către o altă origine, ambele părți trebuie să fie de acord:

  • Frontend-ul setează credentials: "include" la fetch sau withCredentials: true în axios.
  • Serverul trimite Access-Control-Allow-Credentials: true.
  • Access-Control-Allow-Origin de pe server trebuie să fie originea exactă, nu *.

Cookie-urile între site-uri au nevoie și de SameSite=None; Secure, iar unele browsere restricționează suplimentar cookie-urile terț. Acesta este încă un motiv să preferi abordarea de la Pasul 5.

Pasul 5: Evită CORS printr-o configurare pe aceeași origine

Adesea cea mai curată soluție este să nu faci deloc cereri între origini. Servește API-ul de pe aceeași origine cu pagina, de exemplu la https://aplicatia-mea.ro/api/...:

  • În dezvoltare, majoritatea serverelor de dezvoltare pot face proxy. Setarea server.proxy din Vite poate trimite /api către backend-ul tău de pe alt port, astfel încât browserul să vorbească doar cu o singură origine.
  • În producție, trimite /api către backend prin setările de rewrite sau proxy ale hostului tău, sau servește frontend-ul și API-ul de pe același server.

Frontend-ul apelează atunci o cale relativă, de exemplu fetch("/api/orders"). Nu există cerere între origini, nu există preflight între origini și nu există probleme cu cookie-urile. Înseamnă și că nu fixezi un domeniu de API hard-codat, care diferă între previzualizare și producție.

Schimbă frontend-ul ca să apeleze backend-ul prin căi relative /api în loc de un URL complet și configurează un proxy de dezvoltare, astfel încât /api să fie trimis către serverul API. Apoi elimină setările CORS care nu mai sunt necesare.

Ce să nu faci

  • mode: "no-cors" în fetch. Nu ocolește nimic; îi dă codului tău un răspuns gol și ilizibil.
  • Extensii de browser care dezactivează CORS. Schimbă doar browserul tău. Fiecare vizitator vede în continuare eroarea.
  • Servicii publice de „proxy CORS” pentru aplicații reale. Pot citi și modifica tot ce trece prin ele și se pot defecta.
  • Access-Control-Allow-Origin: * pe un API care folosește autentificare. Nu funcționează oricum cu credențiale și este un default greșit pentru date private.
  • Reflectarea oricărui header Origin cu credențiale permise. Asta lasă orice site web să facă cereri autentificate în numele utilizatorilor tăi. Folosește o listă de permise.

Cauze frecvente în aplicațiile construite cu AI

SituațieSoluție
Frontend-ul apelează un API terț cu o cheie secretăMută apelul în backend
Funcționează în previzualizare, nu merge pe domeniul publicatAdaugă originile publicate și ale domeniului personalizat în listă sau folosește căi relative /api
Eroare doar la POST, PUT sau DELETEPreflight-ul nu este tratat; adaugă gestionarea OPTIONS și header-ele permise
Eroarea a apărut după ce ai adăugat autentificareaCredențialele cer o origine exactă și Allow-Credentials
„Eroare CORS”, dar tab-ul Network arată 500Repară întâi eroarea de server
URL-ul API-ului este hard-codat la localhostFolosește căi relative sau o setare per mediu

CORS în Mythex

Când un proiect Mythex are un site web și un server API, ambele rulează ca servicii în același proiect, iar între ele poți comuta în previzualizare. Dacă îi ceri agentului ca frontend-ul să apeleze backend-ul printr-o cale relativă /api, proxy-uită în dezvoltare, eviți majoritatea problemelor CORS. Dacă ai nevoie totuși de apeluri între origini, dă agentului eroarea exactă din consolă și originile implicate. Verifică rezultatul și pe aplicația publicată, pentru că adresa ei este diferită de cea din previzualizare. Vezi Servicii multiple și Backend-uri API în documentație, precum și ghidurile noastre despre cum depanezi o aplicație construită cu AI și frontend vs backend.

Lista de verificare CORS

  • Am citit eroarea completă din consolă și codul de status al cererii care a eșuat
  • Am exclus un 404 sau un 500 ascuns
  • API-urile cu cheie secretă sunt apelate din backend, nu din browser
  • Serverul trimite Access-Control-Allow-Origin cu originile exacte permise
  • Preflight-ul OPTIONS primește răspuns cu metodele și header-ele permise, înainte de autentificare
  • Header-ele CORS sunt prezente și în răspunsurile de eroare
  • Credențiale: origine exactă, Allow-Credentials: true, setări corecte pentru cookie-uri
  • Originile de previzualizare, publicare și domeniu personalizat sunt acoperite sau s-au folosit căi relative /api
  • Nu s-au folosit no-cors, proxy-uri publice sau reflectarea oricărei origini

Întrebări

Ce înseamnă o eroare CORS?

Pagina ta, încărcată de la o origine, a încercat să citească un răspuns de la o altă origine, iar răspunsul serverului respectiv nu includea header-ele care permit asta. Browserul îi blochează codului tău citirea răspunsului. Soluția este pe server sau în modul în care este rutată cererea, nu în codul frontend-ului.

Pot repara o eroare CORS din frontend?

Nu cum trebuie. Setări precum mode: 'no-cors' doar ascund răspunsul de codul tău, care rămâne ilizibil. Repari făcând serverul să trimită header-ul corect Access-Control-Allow-Origin sau apelând API-ul din propriul backend ori dintr-un proxy pe aceeași origine.

Este Access-Control-Allow-Origin: * sigur?

Este în regulă pentru date publice pe care oricine le poate citi, de exemplu un API public fără autentificare. Nu poate fi folosit cu cookie-uri sau alte credențiale, iar pentru API-uri private ar trebui să listezi originile exacte permise.

De ce funcționează cererea în Postman sau curl, dar nu în browser?

CORS este aplicat doar de browsere. Unelte precum Postman și curl nu îl aplică, deci o cerere poate reuși acolo și totuși să fie blocată când JavaScript-ul paginii o face.

Ce este o cerere preflight?

Pentru cererile care nu sunt simple, cum ar fi un POST JSON sau o cerere cu header-ul Authorization, browserul trimite mai întâi o cerere OPTIONS ca să întrebe serverul ce metode și header-e sunt permise. Dacă cererea OPTIONS eșuează sau nu are header-ele potrivite, cererea reală nu se mai trimite niciodată.

Mythex · Pricing · Construiește orice cu AI · Șabloane pentru site-uri și aplicații · Mythex comparat · Glosar pentru crearea de aplicații cu AI · Instrumente gratuite pentru crearea de aplicații cu AI · 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) Русский Slovenčina Slovenščina Soomaali Shqip Srpski Svenska Kiswahili தமிழ் తెలుగు ไทย Tagalog Türkçe Українська اردو Tiếng Việt 简体中文 繁體中文(香港) 繁體中文(台灣)