Vadovai

Kaip ištaisyti CORS klaidas (ir kodėl jos atsiranda)

CORS klaida reiškia, kad jūsų žiniatinklio puslapis bandė nuskaityti atsakymą iš kitos kilmės (kito domeno, prievado ar protokolo), o tas serveris nesiuntė antraščių, sakančių, kad tai leidžiama. Naršyklės blokuoja atsakymą, kad apsaugotų naudotojus. Ją ištaisote serveryje grąžindami Access-Control-Allow-Origin antraštę jūsų svetainės kilmei ir apdorodami OPTIONS išankstines (preflight) užklausas, arba ją apeinate siųsdami užklausą per savo galinę dalį tos pačios kilmės ribose.

Tai viena labiausiai painiųjų klaidų žiniatinklio kūrime, nes užklausa dažnai sėkmingai pasiekia serverį. Šis vadovas paaiškina, kas vyksta ir kaip ištaisyti kiekvieną variantą.

Ką reiškia „kilmė“

Kilmė – tai protokolo, domeno ir prievado derinys:

URLKilmė
https://myapp.com/dashboardhttps://myapp.com
https://api.myapp.com/usershttps://api.myapp.com (kitas subdomenas, todėl kita kilmė)
http://localhost:5173http://localhost:5173
http://localhost:3000http://localhost:3000 (kitas prievadas, todėl kita kilmė)

Naršyklės laikosi tos pačios kilmės politikos: puslapio JavaScript gali laisvai skaityti atsakymus iš savo kilmės, bet ne iš kitų. CORS (Cross-Origin Resource Sharing) – tai sistema, kuri leidžia serveriui pasirinkti: „atsakymus iš manęs gali skaityti puslapiai iš šių kilmių.“

Du padariniai daugelį nustemba:

  • Užklausa dažnai pasiekia serverį. CORS paprastai blokuoja jūsų kodą skaityti atsakymą, o ne serverį gauti užklausą. POST gali būti išsaugotas, nors matėte klaidą.
  • Tik naršyklės tai vykdo. curl, Postman ir serverio pusės kodas CORS visiškai ignoruoja. Todėl „veikia Postman“ nieko nesako apie CORS.

1 žingsnis: perskaitykite tikslią klaidą

Atidarykite kūrėjo įrankius (dešiniuoju pelės klavišu → Inspect), eikite į Console ir perskaitykite visą pranešimą. Chrome formuluotė parodo, kokia problema:

Pranešimas apimaKas negerai
No 'Access-Control-Allow-Origin' header is presentServeris visai nesiuntė antraštės arba užklausa nepavyko dar nepasiekusi jūsų CORS kodo (pavyzdžiui 404 ar 500)
The 'Access-Control-Allow-Origin' header has a value '...' that is not equal to the supplied originServeris leidžia kitą kilmę nei jūsų
Response to preflight request doesn't pass access control checkNepavyko OPTIONS išankstinė užklausa
...must not be the wildcard '*' when the request's credentials mode is 'include'Siunčiate slapukus, o serveris atsakė *
Request header field authorization is not allowedServeris nenurodė tos antraštės Access-Control-Allow-Headers

Tada atidarykite Network skirtuką, raskite nepavykusią užklausą (ir bet kokią prieš ją buvusią OPTIONS užklausą) ir pažiūrėkite jos statuso kodą bei atsakymo antraštes.

Pirmiausia patikrinkite statuso kodą. Daugelis „CORS klaidų“ iš tiesų yra serverio klaidos prisidengusios. Jei serveris sugenda arba grąžina 404, jo klaidos atsakyme dažnai nėra CORS antraščių, todėl naršyklė praneša apie CORS, o ne apie tikrąją problemą. Ištaisykite 500 ar 404, ir CORS klaida gali išnykti.

2 žingsnis: nuspręskite, ar apskritai reikia kviesti tą API iš naršyklės

Prieš pridedant antraštes, paklauskite: ar naršyklė turėtų tiesiogiai kviesti šį API?

  • Trečiosios šalies API, kuriam reikia slaptojo rakto (OpenAI, Stripe slaptasis raktas, dauguma el. pašto paslaugų), niekada neturėtų būti kviečiamas iš naršyklės. Raktas būtų matomas kiekvienam lankytojui. Kvieskite jį iš savo galinės dalies, ir CORS problema išnyks, nes serverio ir serverio užklausoms CORS netaikomas. Žr. kaip saugiai laikyti API raktus.
  • Trečiosios šalies API, kurio nekontroliuojate ir kuris nesiunčia CORS antraščių, iš jūsų pusės neištaisysite. Kvieskite iš savo galinės dalies.
  • Jūsų pačių API galima ištaisyti tinkamai jį sukonfigūravus. Tęskite žemiau.

3 žingsnis: ištaisykite serveryje

Serveris turi pridėti teisingas antraštes savo atsakymams, įskaitant klaidų atsakymus.

Tikrai užklausai siųskite:

  • Access-Control-Allow-Origin: https://myapp.com (tiksli jūsų priekinės dalies kilmė, be galinio brūkšnio)
  • Vary: Origin, jei reikšmė priklauso nuo to, kuri kilmė paprašė

Išankstinei OPTIONS užklausai atsakykite sėkmės statusu (200 arba 204) ir:

  • Access-Control-Allow-Origin: https://myapp.com
  • Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE
  • Access-Control-Allow-Headers: Content-Type, Authorization (kiekvieną jūsų siunčiamą pasirinktinę antraštę)
  • Neprivaloma Access-Control-Max-Age: 600, kad naršyklė išankstinę užklausą talpintų kuriam laikui

Dauguma karkasų turi paketą ar nustatymą, kuris tai atlieka. Express – paketas cors; FastAPI – CORSMiddleware; ASP.NET Core – AddCors; Spring – @CrossOrigin arba CORS konfigūracija. Tipinis Express nustatymas:

import cors from "cors";

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

CORS apdorojimą dėkite prieš savo maršrutus ir autentifikaciją, kad išankstinės užklausos (kurios neturi prisijungimo) nebūtų atmetamos su 401.

Naudodami DI kūrimo įrankį, tiksliai aprašykite nustatymą:

Priekinė dalis https://myapp.com kviečia API https://api.myapp.com ir gauna „Response to preflight request doesn't pass access control check“. Sukonfigūruok CORS API taip, kad leistų tiksliai https://myapp.com ir peržiūros kilmę, leistų GET, POST, PATCH, DELETE su Content-Type ir Authorization antraštėmis, apdorotų OPTIONS prieš autentifikaciją ir įtrauktų CORS antraštes ir į klaidų atsakymus.

4 žingsnis: tvarkykite slapukus ir prisijungimo duomenis

Jei jūsų priekinė dalis siunčia slapukus (pavyzdžiui, prisijungimo sesiją) į kitą kilmę, abi pusės turi sutarti:

  • Priekinė dalis fetch nustato credentials: "include", arba axios – withCredentials: true.
  • Serveris siunčia Access-Control-Allow-Credentials: true.
  • Serverio Access-Control-Allow-Origin turi būti tiksli kilmė, o ne *.

Tarpsvetainiams slapukams taip pat reikia SameSite=None; Secure slapuke, o kai kurios naršyklės trečiųjų šalių slapukus riboja dar labiau. Tai dar viena priežastis teikti pirmenybę 5 žingsnio būdui.

5 žingsnis: venkite CORS naudodami tos pačios kilmės sąranką

Dažnai švariausias pataisymas – visai nesiųsti tarpkilmių užklausų. Teikite API iš tos pačios kilmės kaip puslapis, pavyzdžiui https://myapp.com/api/...:

  • Kūrimo metu dauguma kūrimo serverių gali veikti kaip tarpinis serveris. Vite server.proxy nustatymas gali perduoti /api jūsų galinei daliai kitame prievade, todėl naršyklė visada kalba tik su viena kilme.
  • Produkcijoje nukreipkite /api į galinę dalį per savo serverio perrašymo ar tarpinio serverio nustatymus arba teikite priekinę dalį ir API iš vieno serverio.

Tada priekinė dalis kviečia santykinį kelią, pavyzdžiui fetch("/api/orders"). Nėra tarpkilmių užklausos, nėra tarpkilmių išankstinės užklausos ir nėra slapukų galvos skausmo. Tai reiškia ir tai, kad neįkoduojate API domeno, kuris skiriasi tarp peržiūros ir produkcijos.

Pakeisk priekinę dalį taip, kad ji kviestų galinę dalį santykiniais /api keliais, o ne pilnu URL, ir nustatyk kūrimo tarpinį serverį, kad /api būtų persiųsta į API serverį. Tada pašalink visus CORS nustatymus, kurių nebereikia.

Ko nedaryti

  • mode: "no-cors" fetch funkcijoje. Tai nieko neapeina; tai suteikia jūsų kodui tuščią, neskaitomą atsakymą.
  • Naršyklės plėtiniai, kurie išjungia CORS. Jie keičia tik jūsų naršyklę. Kiekvienas lankytojas vis tiek matys klaidą.
  • Vieši „CORS tarpinio serverio“ paslaugos tikroms programėlėms. Jos gali skaityti ir keisti viską, kas per jas praeina, ir jos dingsta.
  • Access-Control-Allow-Origin: * API, kuris naudoja prisijungimus. Su prisijungimo duomenimis tai vis tiek neveikia, ir tai neteisingas numatytasis nustatymas privatiems duomenims.
  • Bet kurios Origin antraštės atspindėjimas atgal su leidžiamais prisijungimo duomenimis. Tai leidžia bet kuriai svetainei siųsti užklausas prisijungusio naudotojo vardu. Naudokite leidžiamų sąrašą.

Dažnos priežastys DI sukurtose programėlėse

SituacijaPataisymas
Priekinė dalis kviečia trečiosios šalies API su slaptuoju raktuPerkelkite kvietimą į galinę dalį
Veikia peržiūroje, neveikia publikuotame domenePridėkite publikuotą ir pasirinktinį domeną prie leidžiamų kilmių sąrašo arba naudokite santykinius /api kelius
Klaida tik POST, PUT ar DELETEIšankstinė užklausa neapdorota; pridėkite OPTIONS apdorojimą ir leidžiamas antraštes
Klaida atsirado po prisijungimo pridėjimoPrisijungimo duomenims reikia tiksliai kilmės ir Allow-Credentials
„CORS klaida“, bet Network skirtuke rodomas 500Pirma ištaisykite serverio klaidą
API URL įkoduotas kaip localhostNaudokite santykinius kelius arba nustatymą kiekvienai aplinkai

CORS Mythex

Kai Mythex projekte yra svetainė ir API serveris, abu veikia kaip paslaugos tame pačiame projekte, ir peržiūroje galite perjungti tarp jų. Paprašius agento, kad priekinė dalis kviestų galinę dalį per santykinį /api kelią, tarpinį serverį naudojant kūrimo metu, išvengiama daugumos CORS problemų. Jei vis tiek reikia tarpkilmių kvietimų, pateikite agentui tikslią klaidą iš konsolės ir dalyvaujančias kilmes. Patikrinkite rezultatą ir publikuotoje programėlėje, nes jos adresas skiriasi nuo peržiūros. Žr. dokumentacijos Kelių paslaugų programėlės ir API galinės dalys, taip pat mūsų vadovus kaip ištaisyti DI sukurtą programėlę ir priekinė dalis prieš galinę dalį.

CORS kontrolinis sąrašas

  • Perskaityta visa konsolės klaida ir nepavykusios užklausos statuso kodas
  • Atmesta paslėpta 404 ar 500
  • API su slaptaisiais raktais kviečiami iš galinės dalies, o ne iš naršyklės
  • Serveris siunčia Access-Control-Allow-Origin su tiksliai leidžiamomis kilmėmis
  • OPTIONS išankstinė užklausa atsakoma su leidžiamais metodais ir antraštėmis, prieš autentifikaciją
  • CORS antraštės yra ir klaidų atsakymuose
  • Prisijungimo duomenys: tiksli kilmė, Allow-Credentials: true, teisingi slapukų nustatymai
  • Peržiūros, publikuota ir pasirinktinio domeno kilmės visos aprėptos arba naudojami santykiniai /api keliai
  • Nėra no-cors, viešų tarpinių serverių ar bet kokios kilmės atspindėjimo trumpųjų kelių

Klausimai

Ką reiškia CORS klaida?

Jūsų puslapis, įkeltas iš vienos kilmės, bandė nuskaityti atsakymą iš kitos kilmės, o to serverio atsakyme nebuvo antraščių, leidžiančių tai. Naršyklė blokuoja jūsų kodą nuo atsakymo nuskaitymo. Pataisymas yra serveryje arba tame, kaip užklausa nukreipiama, o ne priekinės dalies kode.

Ar galiu ištaisyti CORS klaidą iš priekinės dalies?

Tinkamai – ne. Nustatymai, pavyzdžiui mode: 'no-cors', tik paslepia atsakymą nuo jūsų kodo. Ištaisote, kai serveris siunčia teisingą Access-Control-Allow-Origin antraštę, arba kai API kviečiate iš savo galinės dalies ar to paties kilmės tarpinio serverio.

Ar Access-Control-Allow-Origin: * yra saugu?

Tinka viešiems duomenims, kuriuos gali skaityti bet kas, pavyzdžiui viešam API be prisijungimo. Jo negalima naudoti su slapukais ar kitais prisijungimo duomenimis, o privatiems API turėtumėte nurodyti tikslias leidžiamas kilmes.

Kodėl mano užklausa veikia Postman ar curl, bet ne naršyklėje?

CORS taikomas tik naršyklėse. Tokie įrankiai kaip Postman ir curl jo netaiko, todėl užklausa ten gali pavykti, o puslapio JavaScript ją siunčiant – būti užblokuota.

Kas yra išankstinė (preflight) užklausa?

Ne paprastoms užklausoms, pavyzdžiui JSON POST ar užklausai su Authorization antraštė, naršyklė pirmiausia siunčia OPTIONS užklausą, klausdama serverio, kokie metodai ir antraštės leidžiami. Jei ši OPTIONS užklausa nepavyksta arba neturi reikiamų antraščių, tikroji užklausa niekada neišsiunčiama.

Mythex · Pricing · Kurkite bet ką su dirbtiniu intelektu · Svetainių ir programėlių šablonai · Mythex palyginimas · Programėlių kūrimo su dirbtiniu intelektu žodynėlis · Nemokami programėlių kūrimo su dirbtiniu intelektu įrankiai · 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 日本語 ქართული Қазақ тілі ಕನ್ನಡ 한국어 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 简体中文 繁體中文(香港) 繁體中文(台灣)