Home / Programming Languages, Databases, and Software Development / CORS: Kenapa Browser …

CORS: Kenapa Browser Menolak Permintaan dan Cara Men-debug-nya

Pernahkah Anda melihat pesan merah yang mengerikan di konsol browser seperti ini: Access to fetch at 'http://api.example.com/data' from origin 'http://localhost:3000' has been blocked by CORS policy?

Bagi banyak developer, CORS (Cross-Origin Resource Sharing) terasa seperti “musuh” yang muncul secara acak dan menghalangi aplikasi mereka untuk mengambil data dari API. Namun, CORS sebenarnya adalah mekanisme keamanan krusial yang diimplementasikan oleh browser untuk melindungi pengguna dari serangan berbahaya.

Artikel ini akan mengupas tuntas mekanisme CORS, kapan browser melakukan “Preflight”, dan bagaimana cara men-debug-nya tanpa harus menebak-nebak.

Ringkasan

  • CORS adalah kebijakan keamanan di sisi browser, bukan di sisi server.
  • Server tidak “menolak” permintaan; server mengirim respon, tetapi browser-lah yang memutuskan apakah aplikasi JavaScript boleh membaca respon tersebut.
  • Simple Request: Permintaan yang langsung dikirim ke server (misal: GET dengan header standar).
  • Preflight Request: Permintaan OPTIONS yang dikirim browser sebelum permintaan asli untuk memastikan server mengizinkan metode atau header tertentu.
  • Solusi utama adalah konfigurasi header Access-Control-Allow-Origin yang tepat di server.
  • Penggunaan Proxy (seperti Nginx atau middleware) adalah solusi umum untuk menghindari masalah CORS selama pengembangan.

Apa itu “Origin”?

Sebelum memahami CORS, kita harus memahami konsep Origin. Sebuah origin didefinisikan oleh tiga komponen:

  1. Protokol (http vs https)
  2. Domain/Host (example.com vs api.example.com)
  3. Port (80 vs 3000)

Jika salah satu dari ketiga hal ini berbeda antara halaman web yang memuat script dan server yang dipanggil, maka permintaan tersebut disebut Cross-Origin.

Contoh:

  • http://localhost:3000 $\rightarrow$ http://localhost:3000/api (Same-Origin)
  • http://localhost:3000 $\rightarrow$ https://localhost:3000/api (Cross-Origin - beda protokol)
  • http://localhost:3000 $\rightarrow$ http://localhost:8080/api (Cross-Origin - beda port)
  • http://app.com $\rightarrow$ http://api.app.com (Cross-Origin - beda subdomain)

Cara Kerja CORS

Saat aplikasi JavaScript melakukan fetch ke origin yang berbeda, browser akan melakukan salah satu dari dua skenario berikut: Simple Request atau Preflight Request.

1. Simple Request

Browser mengirim permintaan langsung ke server. Permintaan ini dikategorikan sebagai “Simple” jika memenuhi syarat:

  • Metode HTTP adalah GET, HEAD, atau POST.
  • Header yang digunakan hanya header standar (seperti Accept, Accept-Language, Content-Language).
  • Jika POST, Content-Type harus salah satu dari: application/x-www-form-urlencoded, multipart/form-data, atau text/plain.

Prosesnya:

  1. Browser mengirim request ke server.
  2. Server memproses request dan mengirim respon beserta header Access-Control-Allow-Origin.
  3. Browser memeriksa header tersebut. Jika header tersebut tidak ada atau tidak cocok dengan origin pengirim, browser akan memblokir respon dan memunculkan error CORS di konsol, meskipun server sebenarnya sudah mengirim data (status 200 OK).

2. Preflight Request (The OPTIONS Request)

Jika permintaan tidak memenuhi syarat “Simple” (misal: menggunakan PUT, DELETE, atau mengirim Content-Type: application/json), browser akan merasa permintaan ini “berbahaya” bagi server yang tidak mendukung CORS.

Untuk mencegah kerusakan data, browser mengirim Preflight Request terlebih dahulu.

Urutan Kejadian:

  1. OPTIONS Request: Browser mengirim request dengan metode OPTIONS ke endpoint tujuan.
    • Header Origin: Menginfokan siapa yang memanggil.
    • Header Access-Control-Request-Method: “Bolehkah saya menggunakan metode POST?”
    • Header Access-Control-Request-Headers: “Bolehkah saya mengirim header Authorization?”
  2. Server Response: Server harus menjawab dengan status 200 atau 204 dan header:
    • Access-Control-Allow-Origin: Mengizinkan origin tersebut.
    • Access-Control-Allow-Methods: Daftar metode yang diizinkan (misal: GET, POST, OPTIONS).
    • Access-Control-Allow-Headers: Daftar header yang diizinkan.
  3. Actual Request: Jika respon preflight sukses, browser baru akan mengirimkan permintaan asli (misal: POST dengan body JSON).

Jika preflight gagal, browser tidak akan pernah mengirim permintaan asli, dan Anda akan melihat error CORS di konsol.

Strategi Perbaikan (The Fix)

Solusi A: Konfigurasi Server (Cara Benar)

Cara paling tepat adalah mengonfigurasi server untuk mengizinkan origin tertentu. Jangan pernah menggunakan Access-Control-Allow-Origin: * di lingkungan produksi kecuali API Anda benar-benar publik.

Contoh implementasi logika server:

  1. Baca header Origin dari request.
  2. Cek apakah origin tersebut ada dalam daftar putih (whitelist).
  3. Jika ya, set header Access-Control-Allow-Origin dengan nilai origin tersebut.

Menurut MDN Web Docs on CORS, konfigurasi yang tepat memastikan bahwa hanya aplikasi tepercaya yang dapat mengakses data sensitif.

Solusi B: Menggunakan Proxy (Cara Development)

Selama pengembangan, seringkali kita tidak punya akses ke server API atau malas mengonfigurasi CORS. Solusinya adalah menggunakan proxy.

Konsep Proxy: Browser $\rightarrow$ Local Proxy (Same-Origin) $\rightarrow$ Remote API (Server-to-Server)

Karena CORS adalah kebijakan browser, permintaan dari server (Proxy) ke server lain (API) tidak terikat aturan CORS.

Jika Anda menggunakan Vite atau Webpack, Anda bisa menambahkan konfigurasi proxy di vite.config.js:

javascript
server: {
  proxy: {
    '/api': {
      target: 'http://api.example.com',
      changeOrigin: true,
      rewrite: (path) => path.replace(/^\/api/, '')
    }
  }
}

Perbandingan: Simple vs Preflight Request

Untuk memudahkan pemahaman, berikut adalah perbandingan antara kedua jenis permintaan CORS:

FiturSimple RequestPreflight Request
Metode HTTPGET, HEAD, POSTPUT, DELETE, PATCH, atau POST (JSON)
HeaderHanya header standarCustom header (misal: Authorization)
AlurLangsung ke serverOPTIONS $\rightarrow$ Request Asli
Respon ServerAccess-Control-Allow-OriginAllow-Origin, Allow-Methods, Allow-Headers
Risiko ServerRendah (standar HTTP)Tinggi (bisa mengubah state/data)

Men-debug CORS dengan DevTools

Jangan hanya melihat pesan error di konsol. Gunakan tab Network di Chrome/Firefox DevTools:

  1. Cari Request OPTIONS: Jika Anda melihat request OPTIONS dengan status merah, berarti preflight gagal. Periksa tab “Response Headers” untuk melihat apa yang dikirim server.
  2. Periksa Header Origin: Pastikan header Origin yang dikirim browser benar-benar sama dengan yang diharapkan server.
  3. Cek Status Code: Ingat, CORS error bisa terjadi meskipun status code adalah 200 OK. Yang menentukan adalah ada atau tidaknya header Access-Control-Allow-Origin.

Kesimpulan

CORS bukan bug, melainkan fitur keamanan yang melindungi pengguna dari serangan seperti Cross-Site Request Forgery (CSRF) pada tingkat dasar. Dengan memahami perbedaan antara Simple Request dan Preflight, Anda bisa menentukan apakah masalah Anda terletak pada konfigurasi metode HTTP, header yang tidak dikenal, atau sekadar salah konfigurasi origin di server.

Untuk referensi lebih mendalam, Anda dapat mempelajari:

  • Fetch Standard dari WHATWG yang mendefinisikan bagaimana browser menangani permintaan jaringan secara detail.

  • RFC 6454 yang merupakan spesifikasi dasar dari CORS.

  • MDN CORS Guide untuk panduan implementasi praktis.

  • Gunakan Origin yang spesifik, hindari * untuk data sensitif.

  • Selalu tangani metode OPTIONS di backend jika Anda menggunakan JSON atau custom headers.

  • Manfaatkan proxy selama development untuk mempercepat workflow.

  • Baca tab Network di DevTools untuk melihat apakah preflight (OPTIONS) terkirim atau tidak.