CORS v kontextu webu: principy a implementace přístupu mezi doménami

CORS v kontextu webu: proč existuje a co řeší

CORS (Cross-Origin Resource Sharing) je HTTP mechanismus, který umožňuje bezpečné sdílení zdrojů mezi různými zdroji (origins). Opírá se o hlavičky v požadavcích a odpovědích, které prohlížeč respektuje při čtení dat z jiné domény, subdomény, portu nebo protokolu. CORS rozšiřuje tzv. Same-Origin Policy (SOP) tak, aby webové aplikace mohly přistupovat k API a statickým zdrojům napříč doménami, aniž by došlo ke kompromitaci bezpečnosti.

Origin a Same-Origin Policy: základní pojmy

  • Origin = kombinace scheme://host:port (např. https://app.example.com:443).
  • Same-Origin Policy zabraňuje skriptům načteným z jednoho zdroje číst citlivé odpovědi z jiného zdroje.
  • CORS umožňuje kontrolované prolomení této bariéry prostřednictvím deklarativních hlaviček na straně serveru.

Aktéři a odpovědnosti

  • Prohlížeč vynucuje pravidla CORS a v případě porušení zablokuje přístup k odpovědi (JS neobdrží tělo odpovědi, v konzoli se zobrazí chybová hláška).
  • Server cílového zdroje rozhoduje, komu (kterému originu) a za jakých podmínek poskytne přístup pomocí CORS hlaviček.
  • CDN/Proxy může měnit hlavičky, kešovat preflight odpovědi a ovlivnit chování (pozor na Vary hlavičku).

Typy požadavků: jednoduché, preflight a credentialed

  • Jednoduché požadavky (bez preflightu): metody GET, HEAD, POST s bezpečnými simple hlavičkami (např. Accept, Content-Type s hodnotami typu application/x-www-form-urlencoded, multipart/form-data nebo text/plain), bez vlastních ne-standardních hlaviček.
  • Preflight: prohlížeč nejprve odešle OPTIONS požadavek s hlavičkami Origin, Access-Control-Request-Method a případně Access-Control-Request-Headers. Server musí odpovědět s povolením, jinak hlavní požadavek nebude proveden.
  • Credentialed (s pověřeními): pokud klient posílá cookies/HTTP autentizaci/klientské certifikáty (fetch s credentials: "include"), server musí odpovědět Access-Control-Allow-Credentials: true a nesmí použít Access-Control-Allow-Origin: *; musí uvést konkrétní origin.

Klíčové hlavičky CORS a jejich význam

  • Origin: automaticky přidávaná klientem; identifikuje původ volajícího.
  • Access-Control-Allow-Origin: odpověď serveru s povoleným originem (konkrétní https://example.com nebo * u ne-credentialed požadavků).
  • Access-Control-Allow-Methods: metody povolené u preflightu (GET, POST, PUT, DELETE, OPTIONS a další).
  • Access-Control-Allow-Headers: ne-simple hlavičky povolené v hlavním požadavku (např. Authorization, X-Requested-With).
  • Access-Control-Expose-Headers: seznam hlaviček, které jsou čitelné z JS (jinak je vidět pouze safe list).
  • Access-Control-Allow-Credentials: true, pokud jsou povoleny cookies/pověření.
  • Access-Control-Max-Age: čas, po který může prohlížeč kešovat výsledek preflightu (snižuje latenci; prohlížeče mají vlastní horní limity).
  • Vary: Origin: velmi důležité u CDN – odpověď se má lišit dle Origin. Bez tohoto riskujete cache poisoning nebo únik hlaviček.

Životní cyklus preflightu krok za krokem

  1. JS (např. fetch) chce provést PUT s hlavičkou Authorization.
  2. Prohlížeč odešle OPTIONS požadavek na stejnou URL s hlavičkami Origin a Access-Control-Request-*.
  3. Server odpoví například Access-Control-Allow-Origin: https://app.example.com, Access-Control-Allow-Methods: PUT, Access-Control-Allow-Headers: Authorization, Access-Control-Max-Age: 600.
  4. Prohlížeč si výsledek preflightu dočasně zapamatuje a vykoná hlavní požadavek.

Běžné topologie: SPA, API a CDN

  • SPA na doméně A volá API na doméně B: nastavte B tak, aby podle whitelistu odrážel Access-Control-Allow-Origin pouze pro známé originy a přidal Vary: Origin.
  • CDN před API: nechte CDN respektovat a přeposílat Origin na původní server; kešujte bezpečně podle Vary a zvažte kešování preflightů.
  • Statické assety (fonty, obrázky, video): pokud se používají napříč doménami (např. fonty), přidejte Access-Control-Allow-Origin a správný Content-Type.

Konfigurace: návrhové vzory bez bezpečnostních děr

  • Reflexe originu s whitelistem: server kontroluje Origin, pokud je v seznamu povolených, vrátí Access-Control-Allow-Origin s danou hodnotou a doplní Vary: Origin. Nikdy nereflektujte libovolný Origin bez kontroly.
  • Credentialed API: použijte konkrétní Access-Control-Allow-Origin (nikoli *) a Access-Control-Allow-Credentials: true. Omezte Allow-Methods a Allow-Headers na minimum nezbytné.
  • Minimalizace preflightu: preferujte simple metody a hlavičky; posílejte JSON jako text/plain jen pokud to bezpečnostní politika dovoluje a rozumíte tomu. Často je lepší akceptovat preflight a správně ho kešovat.

Příklady hlaviček pro API odpovědi

Ne-credentialed, více domén přes CDN: Access-Control-Allow-Origin: *, Access-Control-Expose-Headers: ETag, Link, Vary: Origin (pokud později přejdete na politiku per-origin).

Credentialed pouze pro aplikaci: Access-Control-Allow-Origin: https://app.example.com, Access-Control-Allow-Credentials: true, Access-Control-Expose-Headers: X-RateLimit-Remaining, Vary: Origin.

Preflight odpověď: Access-Control-Allow-Origin: https://app.example.com, Access-Control-Allow-Methods: GET, POST, Access-Control-Allow-Headers: Authorization, Content-Type, Access-Control-Max-Age: 600, Vary: Origin, Access-Control-Request-Method, Access-Control-Request-Headers.

Redirecty, cache a CORS

  • Přesměrování mohou narušit CORS, pokud prostředník odstraní hlavičky nebo změní protokol/host/port. Ideální je odpovídat přímo bez 302/301, zejména u preflightu.
  • CDN cache: vždy uvažujte o Vary na Origin a případně i na Access-Control-Request-* pro preflight.
  • ETag a podmíněné požadavky (If-None-Match) fungují s CORS pouze pokud se CORS hlavičky zachovávají i u 304 odpovědí.

Bezpečnostní souvislosti: CORS ≠ autentizace

  • CORS je mechanismus prohlížeče; nenahrazuje autentizaci ani autorizaci. Server stále musí validovat tokeny, session a ACL.
  • Nikdy nepovolujte * současně s Allow-Credentials: true. Prohlížeče to blokují; jde i o bezpečnostní princip.
  • Oddělte CORS od politik CSP, COOP/COEP/CORP – tyto související mechanismy řeší jiné oblasti (izolaci, čitelnost, cross-origin embedding).

Dopad na SEO, AIO/AEO a LLM optimalizaci

  • Indexace a rendering: Googlebot s Web Rendering Service sice vykonává JS, ale API volání přes CORS mohou selhat a způsobit neúplný obsah. Důležitá data pro SEO (např. produktové popisy, FAQ) raději generujte SSR/SSG a nespoléhejte na runtime CORS volání.
  • Structured Data: neodkazujte na JSON-LD přes cross-origin runtime fetch; vložte strukturovaná data přímo do HTML.
  • AIO/AEO (odpovědi asistentů): pokud widgety (HowTo, FAQ, Product) závisí na cross-origin médiích (obrázky, video), povolte CORS na assetové doméně, jinak embed komponenty v SPA mohou zobrazit nefunkční stav.
  • Výkon: preflight zvyšuje round-trip time; při vysokém podílu CORS volání se prodlužuje TTI a zhoršují Core Web Vitals. Zvažte konsolidaci API, HTTP/2/3, kešování preflightu a same-origin proxy.

Diagnostika a testování

  • DevTools → Síť (Network): filtrujte OPTIONS, kontrolujte přítomnost správných CORS hlaviček jak u preflightu, tak u hlavní odpovědi.
  • curl: simulujte preflight například pomocí curl -i -X OPTIONS https://api.example.com/resource -H "Origin: https://app.example.com" -H "Access-Control-Request-Method: PUT" -H "Access-Control-Request-Headers: Authorization".
  • Logy serveru/CDN: zapněte logování pro OPTIONS, sledujte cache hit/miss a chování Vary.
  • Monitoring: měřte procento CORS chyb v JS (window error handler) a korelujte s konverzemi.

Nejčastější chyby a jejich řešení

  1. „No ‚Access-Control-Allow-Origin‘ header is present“ – server nevrací povolení pro daný origin; přidejte správný Access-Control-Allow-Origin a Vary: Origin.
  2. „The value of the ‚Access-Control-Allow-Origin‘ header contains ‚*‘ when credentials flag is true“ – použijte konkrétní origin místo * a ponechte Allow-Credentials: true.
  3. Preflight zablokován 301/302 – odpovídejte přímo nebo přesměrujte pouze hlavní požadavek; preflight by měl dostat definitivní odpověď s CORS hlavičkami.
  4. Chybějící hlavičky při 304 – i 304 musí obsahovat relevantní CORS hlavičky.
  5. CDN odstraňuje hlavičky – povolte a přeposílejte Origin a zachovejte CORS hlavičky na edge serverech.
  6. Příliš široké Allow-Headers/Methods – omezte seznam na nezbytné hodnoty, čímž snížíte útokový povrch.

Optimalizace výkonu: jak snížit latenci CORS

  • Seskupujte volání a preferujte GET u čitelných zdrojů s agresivním cache-control.
  • Pro preflight použijte rozumnou hodnotu Access-Control-Max-Age a zvažte jeho kešování na CDN.
  • Přesuňte API pod stejný origin (reverse proxy, /api na stejné doméně), pokud je to možné.
  • Minimalizujte vlastní hlavičky; Authorization často spouští preflight – zvažte alternativní modely (např. cookie s SameSite nastavením a CSRF ochranou), vždy však s ohledem na bezpečnost.

Specifika cookies a SameSite

  • Pro cross-site cookies je obvykle nutné SameSite=None; Secure. Jinak prohlížeč cookie nepošle, i když CORS povoluje credentials.
  • Nezapomeňte sladit doménu cookie (Domain) s tím, jak ji chcete používat napříč subdoménami.

Fonty, obrázky a média napříč doménami

  • Webové fonty vyžadují správné CORS hlavičky na assetové doméně (Access-Control-Allow-Origin), jinak může dojít k jejich zablokování.
  • Při načítání pomocí <img> proběhne načtení, ale čtení pixelových dat z canvasu bez CORS povolení vede k znečištění canvasu (ochrana soukromí).
  • Video a audio streamy mohou potřebovat kompatibilní CORS i pro Range požadavky a správné MIME typy.

Integrace do vývojového procesu

  • Infra jako kód: definujte CORS politiky v Nginx/Apache/CloudFront/S3 konfiguraci jako součást repozitáře.
  • Testy: přidejte integrační testy, které ověřují přítomnost správných hlaviček pro běžné i preflight scénáře.
  • Observabilita: sledujte metriky pro