İçeriğe atla
Bizi takip edin:
ÖZEL YAZILIM VE ENDÜSTRİYEL ENTEGRASYON
KURUMSAL MİMARİ KOCAELİ & GEBZE SANAYİ BÖLGESİ

CORS (Cross-Origin) Hatası Neden Olur ve Nasıl Çözülür?

HEDEF SAHA & SEKTÖR Talaşlı İmalat, Otomotiv & Makine Sanayi
MİMARİ & STANDART Özel İmalat ERP · BOM · Saha WMS
TEMEL İŞ KAZANIMI Süreç Hızı ve Sıfır Manuel Hata
DOĞRULANMIŞ HAP BİLGİ & ÇÖZÜM (TL;DR)

Modern web geliştirmede CORS hatası çözümü: Preflight OPTIONS istekleri, Access-Control başlıkları, API Gateway ve güvenlik yapılandırma kılavuzu burada.

Hızlı Özet (TL;DR)

Modern web uygulama geliştirmede (React, Vue, Angular, Astro veya kurumsal SPA projelerinde) geliştiricilerin konsolda en sık karşılaştığı mesajlardan biri Access to fetch at '...' from origin '...' has been blocked by CORS policy uyarısıdır. CORS (Cross-Origin Resource Sharing - Kökenler Arası Kaynak Paylaşımı), bir web sayfasının (örneğin https://portal.maysayazilim.com) farklı bir etki alanındaki (https://api.maysayazilim.com) kaynaklara tarayıcı üzerinden erişmesini denetleyen standart bir HTTP güvenlik mekanizmasıdır. CORS bir sunucu arızası veya kod hatası değil, tarayıcının kullanıcıyı korumak için uyguladığı Aynı Köken Politikası (SOP - Same-Origin Policy) kalkanıdır. Kalıcı ve güvenli çözüm; tarayıcı eklentileriyle kuralı delmeye çalışmak değil, arka uç (Backend) sunucusunda veya API Gateway katmanında Access-Control-Allow-Origin başlıklarını doğru yapılandırmak ve OPTIONS ön uçuş (Preflight) isteklerine standartlara uygun yanıt dönmektir.

Same-Origin Policy (Aynı Köken Politikası) Nedir ve Neden Vardır?

CORS mekanizmasını anlamak için önce tarayıcıların neden varsayılan olarak farklı siteler arası veri alışverişini engellediğini bilmek gerekir. Eğer tarayıcılarda Same-Origin Policy (SOP) olmasaydı; siz bir sekmede şirketinizin ERP yönetim paneline veya internet bankacılığına giriş yapmışken, yan sekmede açtığınız zararlı bir blog sitesi arka planda gizlice JavaScript (fetch veya XMLHttpRequest) çalıştırarak sizin oturum çerezlerinizle ERP API’nize istek atabilir, tüm müşteri veritabanınızı okuyabilir veya para transferi başlatabilirdi.

Tarayıcı bu felaketi önlemek için bir isteğin “Aynı Köken” (Same-Origin) sayılıp sayılmayacağını URL’nin 3 temel bileşenine bakarak denetler:

  1. Protokol (Scheme): http:// ve https:// tamamen farklı kökenlerdir.
  2. Domain / Host (Alan Adı): maysayazilim.com, www.maysayazilim.com ve api.maysayazilim.com birbirinden farklı kökenlerdir.
  3. Port Numarası: http://localhost:3000 (Frontend) ve http://localhost:8080 (Backend API) port numaraları farklı olduğu için farklı kökenlerdir.

Köken (Origin) Karşılaştırma Matrisi

Referans adresimiz https://app.maysayazilim.com:443/siparisler olduğunda tarayıcının diğer adreslere verdiği karar tablosu:

Hedef İstek URL AdresiKöken DurumuTeknik Gerekçe
https://app.maysayazilim.com/urunlerSame-Origin (İzin Verilir)Protokol (https), alan adı (app.maysayazilim.com) ve port (443) birebir aynıdır
http://app.maysayazilim.com/urunlerCross-Origin (CORS Devrede)Protokol farklıdır (https yerine şifresiz http kullanılmış)
https://api.maysayazilim.com/v1/stokCross-Origin (CORS Devrede)Alt alan adı (Subdomain) farklıdır (app yerine api)
https://app.maysayazilim.com:8443/stokCross-Origin (CORS Devrede)Port numarası farklıdır (Varsayılan 443 yerine 8443)
https://erp-partner.com/api/faturaCross-Origin (CORS Devrede)Kök alan adı (Root Domain) tamamen farklıdır

Basit İstekler (Simple Requests) vs Ön Uçuşlu İstekler (Preflighted Requests)

Tarayıcı her Cross-Origin isteğinde aynı prosedürü işletmez. W3C ve Fetch spesifikasyonuna göre istekler ikiye ayrılır:

1. Basit İstekler (Simple Requests)

Eğer yapılan HTTP isteği sadece GET, HEAD veya POST metodunu kullanıyorsa, özel bir HTTP başlığı (örneğin Authorization: Bearer ... veya X-Custom-Token) taşımıyorsa ve Content-Type değeri yalnızca klasik form tiplerinden (application/x-www-form-urlencoded, multipart/form-data veya text/plain) biriyse tarayıcı ön uçuş yapmaz. İsteği doğrudan gönderir ve sadece dönen yanıtta Access-Control-Allow-Origin başlığının olup olmadığını kontrol eder.

2. Preflight (Ön Uçuş) OPTIONS İsteği Nasıl Çalışır?

Modern kurumsal yazılımlarda ve imalat ERP ve üretim takip çözümleri API’lerinde neredeyse tüm istekler Content-Type: application/json gövdesi ve Authorization kimlik doğrulama başlığı taşır veya PUT, DELETE, PATCH metotlarını kullanır. Bu durumda tarayıcı asıl veriyi göndermeden hemen önce sunucuya otomatik olarak bir Keşif / Ön Uçuş İsteği (Preflight Request) gönderir:

OPTIONS /api/v1/siparisler HTTP/1.1
Host: api.maysayazilim.com
Origin: https://app.maysayazilim.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type, Authorization

Burada tarayıcı sunucuya şunu sorar: “Ben şu an https://app.maysayazilim.com sayfasındayım; birazdan sana POST metoduyla ve Authorization başlığıyla gerçek bir istek göndereceğim. Buna izin veriyor musun?”

Sunucu bu OPTIONS isteğinde veritabanı sorgusu çalıştırmamalı veya kimlik doğrulama (Auth Middleware) hatası vermemelidir; doğrudan aşağıdaki başlıklarla boş gövdeli bir HTTP 204 No Content (veya 200 OK) yanıtı dönmelidir:

HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://app.maysayazilim.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 86400

Buradaki Access-Control-Max-Age: 86400 başlığı kritik bir performans optimizasyonudur: Tarayıcıya bu izin bilgisini 24 saat (86.400 saniye) boyunca önbelleğe almasını söyler; böylece kullanıcı her butona tıkladığında tekrar tekrar OPTIONS isteği atılmaz ve API yanıt süresi iki kat hızlanır.

CORS Yapılandırmasında Yapılan Tehlikeli Hata: * (Joker Karakter)

Sahada gerçekleştirdiğimiz sızma testi ve pentest denetimlerinde en sık tespit ettiğimiz güvenlik zafiyetlerinden biri; geliştiricilerin geliştirme aşamasında karşılaştıkları CORS hatasından hızlıca kurtulmak için üretim (Production) ortamında Access-Control-Allow-Origin: * (Wildcard / Joker Karakter) tanımlaması veya gelen Origin başlığını hiç doğrulamadan aynen geri yansıtmasıdır (Origin Reflection).

  • Kimlik Bilgileriyle (Credentials) Çatışma: Eğer uygulamanız oturum çerezleri (cookies) veya kimlik doğrulama başlıkları kullanıyorsa ve istemci tarafında credentials: 'include' parametresi açıksa, tarayıcı güvenlik standartları gereği Access-Control-Allow-Origin: * yanıtını kesinlikle reddeder. Bu durumda yıldız karakteri yerine tam alan adı (https://app.maysayazilim.com) açıkça yazılmalıdır.
  • Yetkisiz Veri Okuma Riski: Özel bir kurumsal API’de joker karakter açıldığında, internetteki herhangi bir zararlı web sitesi kullanıcının tarayıcısı üzerinden API’nize sorgu atıp dönen yanıtı okuyabilir.
  • Doğru Mühendislik Yaklaşımı: Sunucu tarafında yalnızca şirketinize ait güvenilir alt alan adlarını içeren katı bir İzin Listesi (Allowlist) dizisi tanımlanmalı ve gelen Origin başlığı bu listeyle eşleşiyorsa yanıt başlığına eklenmelidir.

Mimari Çözümler: Node.js, Nginx ve Reverse Proxy Yaklaşımı

Kurumsal projelerde CORS yapılandırmasını yönetmenin üç temiz yolu vardır:

1. Backend Uygulama Katmanında (Node.js / Express / NestJS) Dinamik Allowlist

Uygulama sunucusunda izin verilen alan adlarını bir dizi içinde kontrol eden güvenli ara katman (Middleware) örneği:

const izinliKokenler = [
  'https://app.maysayazilim.com',
  'https://bayi.maysayazilim.com'
];

app.use((req, res, next) => {
  const origin = req.headers.origin;
  if (izinliKokenler.includes(origin)) {
    res.setHeader('Access-Control-Allow-Origin', origin);
    res.setHeader('Vary', 'Origin'); // CDN önbellek karışıklığını önler!
  }
  res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
  res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
  res.setHeader('Access-Control-Allow-Credentials', 'true');

  if (req.method === 'OPTIONS') {
    return res.sendStatus(204);
  }
  next();
});

Kritik Mühendislik Notu: Access-Control-Allow-Origin değeri dinamik olarak değiştirildiğinde yanıt başlığına mutlaka Vary: Origin eklenmelidir. Aksi takdirde Cloudflare veya ara önbellek (Cache) sunucuları bir alt alan adı için üretilen CORS başlığını önbelleğe alıp diğer alt alan adına yanlış başlık sunabilir.

2. Reverse Proxy (Ters Vekil Sunucu) ile CORS İhtiyacını Tamamen Ortadan Kaldırma

En temiz kurumsal mimari, ön uç (Frontend) ile arka uç (Backend API) servislerini aynı alan adı altında birleştirmektir. Örneğin ön yüzünüz https://portal.fabrikam.com adresinde çalışırken, Nginx veya API Gateway üzerinde /api/ yoluna gelen istekleri arka plandaki iç sunucuya (http://10.0.0.50:8080) yönlendirirsiniz (Reverse Proxy). Böylece tarayıcı açısından hem web sayfası hem de API istekleri https://portal.fabrikam.com kökeninden yapıldığı için (%100 Same-Origin) hiçbir CORS hatası veya OPTIONS ön uçuş gecikmesi yaşanmaz.

Güvenli frontend ve backend mimarileriyle tasarlanmış, yüksek performanslı web projeleri için Web Sayfası Tasarım ve Özel Yazılım Geliştirme sayfalarımızı ziyaret edebilirsiniz.

Sıkça Sorulan Sorular

API isteğim Postman veya cURL ile çalıştırdığımda sorunsuz yanıt veriyor ama tarayıcıda CORS hatası alıyor, neden?

Çünkü Same-Origin Policy (Aynı Köken Politikası) ve CORS kuralları yalnızca web tarayıcıları (Chrome, Firefox, Safari, Edge) tarafından uygulanan bir güvenlik mekanizmasıdır. Postman, Insomnia, cURL veya sunucudan sunucuya (Backend-to-Backend) çalışan bir Python/Node.js servisi tarayıcı olmadığı için Origin başlığı göndermez ve CORS kısıtlamasına takılmaz. İsteğin Postman’de çalışıp tarayıcıda çalışmaması, sunucunuzun aktif olduğunu ancak tarayıcının beklediği Access-Control-Allow-Origin yanıt başlığını dönmediğini kanıtlar.

Geliştirme yaparken yerel bilgisayarda (localhost) CORS hatasını en doğru nasıl aşarız?

Yerel geliştirme sırasında tarayıcıya “Disable CORS” gibi güvenlik kapatıcı eklentiler kurmak hem tehlikelidir hem de ekip arkadaşlarınıza kodu gönderdiğinizde hatanın devam etmesine neden olur. Bunun yerine Vite, Webpack, Astro veya Next.js gibi modern araçların yerleşik Dev Server Proxy özelliği kullanılmalıdır. Yapılandırma dosyasında /api istekleri hedef backend adresine proxy olarak tanımlandığında, yerel geliştirme sunucusu istekleri sunucu katmanında iletir ve tarayıcı hiçbir CORS engeline takılmaz.

Sunucumda CORS başlıkları doğru ayarlı olmasına rağmen 500 veya 401 hatası aldığımda neden ekranda yine CORS hatası görünüyor?

Bu durum geliştiricileri en çok yanıltan senaryodur. Eğer arka uç sunucunuzda bir veritabanı çökmesi (HTTP 500 Internal Server Error) yaşanır veya Nginx 502 Bad Gateway dönerse, istek normal uygulama akışına (ve dolayısıyla sizin yazdığınız CORS middleware koduna) hiç ulaşmadan sunucu tarafından hata sayfasıyla kesilir. Hata yanıtının içinde Access-Control-Allow-Origin başlığı bulunmadığı için tarayıcı asıl 500/502 hatasını gizler ve konsola CORS hatası basar. Bu nedenle CORS başlıkları sadece başarılı (200) yanıtlara değil, hata (always parametresiyle 4xx ve 5xx) yanıtlarına da eklenecek şekilde yapılandırılmalıdır.

BİLGİ BANKASI & VAKALAR

İlginizi Çekebilecek Diğer Rehberler

Sanayi dijitalleşmesi, siber güvenlik ve kurumsal yazılım alanında kapsamlı analizler.

Uygulama Hizmeti

Web Sayfası Tasarım

Bu makaledeki metodolojinin anahtar teslim proje adımları ve teknik teslim detayları.

Hizmeti inceleyin

İşinize uygun çözümü birlikte çıkaralım

İhtiyacınızı dinleyelim, kapsamı ve bütçeyi netleştirelim. İlk görüşme ücretsizdir.

Teklif Formu

Size buradan dönüş yapacağız; WhatsApp numaranız olması işimizi kolaylaştırır.

Talebiniz Alındı!

Mesajınız info@maysayazilim.com adresine başarıyla iletildi.
En kısa sürede sizinle iletişime geçeceğiz.