DEV Community

Cover image for CORS Hataları Nasıl Giderilir: Access-Control-Allow-Origin Hata Ayıklama
Tobias Hoffmann
Tobias Hoffmann

Posted on Originally published at apidog.com

CORS Hataları Nasıl Giderilir: Access-Control-Allow-Origin Hata Ayıklama

Yeni bir frontend yayınlarsınız, konsolu açarsınız ve karşınızda kırmızı bir CORS hatası belirir: İstek “CORS politikası tarafından engellendi.” API, Apidog veya curl ile sorunsuz çalışırken tarayıcı yanıtı JavaScript'e iletmeyi reddeder. Sorunun kaynağını öğrendiğinizde bu durum gizemli olmaktan çıkar.

Bugün Apidog'u deneyin

Temel gerçek şudur: CORS hatası tarayıcı tarafından uygulanır, ancak sunucu tarafından kaynaklanır. Sunucunuz doğru Access-Control-Allow-Origin başlığını göndermediğinde tarayıcı yanıtı engeller. Bu nedenle çözüm çoğunlukla frontend kodunda değil, sunucu yapılandırmasındadır.

Bu rehberde CORS'un ne olduğunu, preflight isteğinin nasıl çalıştığını, en yaygın altı hata mesajını ve Express, Spring Boot ve Nginx yapılandırmalarını inceleyeceğiz. Ayrıca tarayıcı dışından hata ayıklama yöntemini de göreceğiz.

CORS hatası nedir?

CORS, Cross-Origin Resource Sharing (Kaynaklar Arası Kaynak Paylaşımı) anlamına gelir.

Tarayıcılar varsayılan olarak aynı kaynak politikasını uygular. Örneğin https://app.example.com üzerinde çalışan JavaScript, şema, ana bilgisayar veya port farklı olduğu için https://api.example.com yanıtlarını okuyamaz.

CORS, sunucuların bu kuralı kontrollü biçimde gevşetmesini sağlayan mekanizmadır. Ayrıntılar için MDN CORS belgelerine ve Fetch spesifikasyonuna bakabilirsiniz.

Çoğu karışıklığı şu üç nokta çözer:

  • Tarayıcı uygular: CORS kontrollerini yalnızca tarayıcılar yapar. Sunucudan sunucuya çağrılar, curl ve masaüstü API istemcileri CORS'u yok sayar.
  • Sunucu yapılandırır: Tarayıcı kararını sunucunun gönderdiği yanıt başlıklarına göre verir.
  • İstek sunucuya ulaşabilir: Basit isteklerde sunucu isteği işler ve yanıt verir; tarayıcı daha sonra bu yanıtı JavaScript'ten gizler.

CORS, API'nizin etrafında bir güvenlik duvarı değildir. Kullanıcıları, çerezleriyle kaynaklar arası verileri okumaya çalışan kötü amaçlı web sayfalarından korur.

Preflight isteği nasıl çalışır?

Bazı kaynaklar arası isteklerden önce tarayıcı bir keşif isteği gönderir. Buna preflight veya ön kontrol isteği denir. Bu istek OPTIONS yöntemiyle yapılır.

Preflight genellikle şu durumlarda tetiklenir:

  • GET, HEAD veya POST dışındaki bir yöntem kullanıldığında
  • Authorization gibi özel başlıklar gönderildiğinde
  • application/json gibi basit olmayan bir Content-Type kullanıldığında

Örnek bir preflight isteği:

OPTIONS /v1/orders HTTP/1.1
Host: api.example.com
Origin: https://app.example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: authorization, content-type
Enter fullscreen mode Exit fullscreen mode

Tarayıcı aslında şunu sorar:

https://app.example.com kaynağındaki bir sayfa, bu başlıklarla POST isteği gönderebilir mi?

Sunucunun doğru yanıtı şöyle olabilir:

HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Authorization, Content-Type
Access-Control-Max-Age: 86400
Vary: Origin
Enter fullscreen mode Exit fullscreen mode

Herhangi bir parça eksikse tarayıcı gerçek isteği göndermeden işlemi iptal eder. API uç noktanız çalışmaz; günlüklerde yalnızca OPTIONS isteğini görürsünüz.

Access-Control-Max-Age, tarayıcıya preflight kararını önbelleğe almasını söyler. Yukarıdaki örnekte süre 86400 saniyedir.

CORS hata ayıklarken ilk sorunuz şu olmalı:

Preflight mı başarısız oldu, yoksa gerçek istek mi?

En yaygın 6 CORS hatası

1. 'Access-Control-Allow-Origin' header is missing

Sunucu yanıtında hiç CORS başlığı yoktur. Tarayıcı değerlendirecek bir bilgi bulamadığı için yanıtı engeller.

Belirli bir frontend kaynağı için:

Access-Control-Allow-Origin: https://app.example.com
Enter fullscreen mode Exit fullscreen mode

Kimlik bilgisi gerektirmeyen herkese açık API'lerde * kullanılabilir.

Bir başka yaygın sorun da hata yanıtlarının CORS başlıklarını içermemesidir. Örneğin başarılı yanıtlar başlıkları içerirken 403 veya 500 yanıtları içermeyebilir. 403 Forbidden dahil olmak üzere tüm yanıtların CORS başlıklarını taşıdığından emin olun.

2. '*' wildcard cannot be used with credentials

Frontend çerez veya kimlik doğrulama bilgileri gönderiyor:

fetch(url, { credentials: 'include' });
Enter fullscreen mode Exit fullscreen mode

Ancak sunucu şu yanıtı veriyor:

Access-Control-Allow-Origin: *
Enter fullscreen mode Exit fullscreen mode

Kimlik bilgileriyle birlikte joker karakter kullanılamaz. Bunun yerine izin verilen kaynağı açıkça belirtin:

Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Credentials: true
Enter fullscreen mode Exit fullscreen mode

Gelen Origin değerini doğrulamadan doğrudan yansıtmayın. Kimlik bilgileri açıkken rastgele kaynakları yansıtmak CORS korumasını ortadan kaldırır.

3. Preflight yanıtı erişim kontrolü denetimini geçemiyor

Sunucu OPTIONS isteğini ele almıyor olabilir:

  • Uç nokta yalnızca POST tanımlıyordur ve OPTIONS için 404 veya 405 dönüyordur.
  • Kimlik doğrulama ara yazılımı, belirteç taşımayan preflight isteğini 401 ile reddediyordur.

Preflight isteğini kimlik doğrulamadan önce işleyin ve gerekli başlıklarla birlikte 2xx yanıt döndürün:

app.options('/v1/orders', (req, res) => {
  res.set({
    'Access-Control-Allow-Origin': 'https://app.example.com',
    'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
    'Access-Control-Allow-Headers': 'Authorization, Content-Type'
  });
  res.sendStatus(204);
});
Enter fullscreen mode Exit fullscreen mode

Çoğu framework'te CORS ara yazılımını kimlik doğrulama ara yazılımından önce bağlamak yeterlidir. Express için resmi cors ara yazılımı belgelerine bakabilirsiniz.

4. Başlık değeri sağlanan kaynağa eşit değil

Sunucu Access-Control-Allow-Origin gönderir, ancak yanlış kaynağı belirtir.

Yaygın nedenler:

  • http://localhost:5173 üzerinden test edilirken yalnızca üretim kaynağının tanımlı olması
  • http ve https arasındaki fark
  • Gereksiz sondaki eğik çizgi: https://app.example.com/

Kaynağı izin listesiyle tam olarak karşılaştırın, eşleşen değeri yansıtın ve Vary: Origin ekleyin:

const allowed = [
  'https://app.example.com',
  'http://localhost:5173'
];

if (allowed.includes(req.headers.origin)) {
  res.set('Access-Control-Allow-Origin', req.headers.origin);
  res.set('Vary', 'Origin');
}
Enter fullscreen mode Exit fullscreen mode

Vary: Origin, önbelleklerin veya CDN'lerin bir kaynak için üretilen başlığı başka bir kaynağa sunmasını önler.

5. İstenen başlık veya yöntem izin verilmiyor

Örnek hata mesajları:

  • authorization başlığı, Access-Control-Allow-Headers tarafından izin verilmiyor.
  • PUT yöntemi, Access-Control-Allow-Methods tarafından izin verilmiyor.

Preflight yanıtı geliyor, ancak frontend'in ihtiyaçlarını karşılamıyordur. Yanıtı kullandığınız tüm yöntem ve başlıkları içerecek şekilde genişletin:

Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE, OPTIONS
Access-Control-Allow-Headers: Authorization, Content-Type, X-Request-Id
Enter fullscreen mode Exit fullscreen mode

Başlık adları büyük/küçük harfe duyarlı değildir. HTTP yöntemlerini ise büyük harfle yazın.

6. Preflight isteği için yönlendirmeye izin verilmiyor

Preflight isteği 301 veya 302 döndüren bir URL'ye ulaştığında tarayıcı yönlendirmeyi takip etmeyebilir.

Yaygın nedenler:

  • http adresinin https adresine yönlendirilmesi
  • Eksik sondaki eğik çizginin framework tarafından eklenmesi
  • /v1/orders adresinin /v1/orders/ adresine yönlendirilmesi

Frontend'i doğrudan nihai URL'ye yönlendirin. HTTPS'i baştan kullanın, yönlendiricinin sondaki eğik çizgi kuralına uyun ve OPTIONS isteğinin 3xx yerine 2xx döndürdüğünü manuel olarak doğrulayın.

Sunucu yapılandırma örnekleri

Express

Başlıkları elle yazmak yerine resmi cors ara yazılımını kullanın:

const express = require('express');
const cors = require('cors');
const app = express();

app.use(cors({
  origin: ['https://app.example.com', 'http://localhost:5173'],
  methods: ['GET', 'POST', 'PUT', 'DELETE'],
  allowedHeaders: ['Authorization', 'Content-Type'],
  credentials: true,
  maxAge: 86400
}));
Enter fullscreen mode Exit fullscreen mode

CORS'u kimlik doğrulama ara yazılımından önce bağlayın. Böylece preflight istekleri eksik belirteç nedeniyle reddedilmez.

Python kullanıyorsanız, aynı başlık mantığını Flask-CORS uzantısı ile uygulayabilirsiniz.

Spring Boot

WebMvcConfigurer üzerinden küresel yapılandırma:

@Configuration
public class CorsConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/v1/**")
            .allowedOrigins("https://app.example.com")
            .allowedMethods("GET", "POST", "PUT", "DELETE")
            .allowedHeaders("Authorization", "Content-Type")
            .allowCredentials(true)
            .maxAge(86400);
    }
}
Enter fullscreen mode Exit fullscreen mode

Spring Security kullanıyorsanız güvenlik filtre zincirinde de CORS'u etkinleştirin:

.cors(Customizer.withDefaults())
Enter fullscreen mode Exit fullscreen mode

Aksi halde güvenlik katmanı, MVC yapılandırmanız devreye girmeden önce preflight isteklerini engelleyebilir. Ayrıntılar için Spring CORS belgelerine bakın.

Nginx

Nginx istekleri uygulamanızın önünde sonlandırıyorsa preflight yanıtını kenarda verebilirsiniz:

location /v1/ {
    if ($request_method = OPTIONS) {
        add_header Access-Control-Allow-Origin "https://app.example.com" always;
        add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" always;
        add_header Access-Control-Allow-Headers "Authorization, Content-Type" always;
        add_header Access-Control-Max-Age 86400 always;
        return 204;
    }

    add_header Access-Control-Allow-Origin "https://app.example.com" always;
    add_header Vary "Origin" always;
    proxy_pass http://backend;
}
Enter fullscreen mode Exit fullscreen mode

always bayrağı önemlidir. Bu bayrak olmadan Nginx, 4xx ve 5xx yanıtlarında add_header yönergelerini uygulamaz.

CORS başlıklarını hangi katmanın yöneteceğine de karar verin. Hem Nginx hem de uygulama başlık eklerse yinelenen değerler, örneğin Access-Control-Allow-Origin: *, *, tarayıcının yanıtı reddetmesine neden olabilir.

Apidog ile tarayıcı dışında CORS hata ayıklama

Konsol hatası yalnızca tarayıcının yanıtı engellediğini söyler; sunucunun gerçekte ne gönderdiğini göstermez.

Apidog bir masaüstü API istemcisi olduğu için istekleri tarayıcının CORS kontrollerine tabi değildir. Frontend'in gönderdiği isteği Apidog'da tekrar ederek sorunun API'de mi, CORS yapılandırmasında mı olduğunu ayırabilirsiniz.

  • Apidog'da istek başarılıysa API mantığı büyük olasılıkla doğrudur; sorun CORS başlıklarındadır.
  • Apidog'da da başarısızsa CORS görünümüne bürünmüş normal bir API hatasıyla karşı karşıyasınız.

Uygulanabilir hata ayıklama akışı

  1. Gerçek isteği tekrar oynatın.

    Tarayıcının Network sekmesindeki başarısız isteği Apidog'da aynı yöntem, başlıklar ve gövdeyle oluşturun. Durum kodunu ve yanıt gövdesini kontrol edin.

  2. Preflight isteğini elle test edin.

    Yeni bir OPTIONS isteği oluşturun ve şu başlıkları ekleyin:

   Origin: https://app.example.com
   Access-Control-Request-Method: POST
   Access-Control-Request-Headers: authorization, content-type
Enter fullscreen mode Exit fullscreen mode
  1. Yanıt başlıklarını inceleyin. Şu başlıkları frontend'in ihtiyaçlarıyla karşılaştırın:
   Access-Control-Allow-Origin
   Access-Control-Allow-Methods
   Access-Control-Allow-Headers
Enter fullscreen mode Exit fullscreen mode

Eksik başlık, yanlış kaynak veya 3xx durum kodu burada hemen görünür.

  1. Düzeltmeyi doğrulayın. Sunucu yapılandırmasını değiştirdikten sonra aynı kayıtlı OPTIONS isteğini yeniden gönderin ve başlıkların güncellendiğini kontrol edin.

Bu iş akışı, “API istemcimde çalışıyor ama tarayıcıda başarısız oluyor” sorununu hızlıca izole eder. İstemci CORS'u atladığı için başarılı olabilir; tarayıcı ise sunucudan gerekli başlıkları alamadığı için yanıtı engeller. Apidog'u ücretsiz indirin ve düzenli endpoint testlerinizin yanına bir OPTIONS isteği kaydedin.

30 saniyelik CORS kontrol listesi

Hatayı araştırmadan önce şunları kontrol edin:

  • Başarısız yanıt Access-Control-Allow-Origin içeriyor mu?
  • Değer sayfanızın kaynağıyla tam olarak eşleşiyor mu? Şema, ana bilgisayar ve port dahil; gereksiz sondaki eğik çizgi olmadan.
  • Çerez veya kimlik doğrulama kullanıyor musunuz? Belirli bir kaynak ve Access-Control-Allow-Credentials: true kullanın; * kullanmayın.
  • OPTIONS isteği, gerekli yöntem ve başlıklarla birlikte 2xx döndürüyor mu?
  • Preflight URL'sinde herhangi bir yönlendirme var mı?
  • 401, 403 ve 500 gibi hata yanıtları da başarılı yanıtlarla aynı CORS başlıklarını taşıyor mu?

On vakadan dokuzunda sorun bu maddelerden biridir. Apidog'da manuel bir OPTIONS isteğiyle doğrulayın, sunucu yapılandırmasını düzeltin ve geliştirmeye devam edin.

SSS

Neden yalnızca tarayıcıda CORS hatası alıyorum?

Çünkü CORS'u yalnızca tarayıcılar uygular. Aynı kaynak politikası, kötü amaçlı sayfaların kimliği doğrulanmış verileri okumasını engeller. curl, backend servisleri ve masaüstü istemcilerinde böyle bir kural yoktur.

İstek tarayıcı dışında başarılı oluyorsa API sağlıklı olabilir; sunucunuzun CORS başlıkları eksik veya yanlış yapılandırılmıştır.

CORS Postman veya Apidog için geçerli mi?

Hayır. Postman ve Apidog, tarayıcı sanal alanında çalışan web sayfaları değil, masaüstü uygulamalarıdır. Bu nedenle CORS'u atlarlar ve sunucunun ham yanıt başlıklarını gösterirler.

Bu araçlar CORS hata ayıklamada kullanışlıdır; ancak masaüstü istemcisinde başarılı bir istek, tarayıcı davranışı hakkında tek başına kanıt değildir. Yalnızca başarısız olan katmanı izole eder.

Daha fazla bilgi için API test teknikleri rehberine ve Postman CORS testi rehberine bakın.

CORS hatası bir güvenlik özelliği mi, yoksa hata mı?

Bir güvenlik özelliğidir. Sunucu açıkça izin vermediği sürece tarayıcı, kaynaklar arası yanıt verilerini script'lere sunmaz.

Tarayıcıda CORS'u bayraklarla veya uzantılarla devre dışı bırakmak yalnızca sorunu kendi makinenizde gizler. Diğer kullanıcılar yine aynı hatayı görür. Kalıcı çözüm sunucu başlıklarını düzeltmektir.

Access-Control-Allow-Origin: * her yerde kullanılabilir mi?

Yalnızca çerez veya kimlik doğrulama içermeyen herkese açık, salt okunur API'lerde kullanın.

Joker karakter kimlik bilgileriyle birlikte reddedilir ve verilerinizi her kaynağa açar. Kimlik doğrulamalı API'lerde:

  1. İzin verilen kaynaklardan oluşan bir liste tutun.
  2. Eşleşen kaynağı yanıtlayın.
  3. Paylaşılan önbelleklerin yanıtları ayırması için Vary: Origin gönderin.

Top comments (0)