DEV Community

Cover image for Cara Mengatasi Error CORS: Debugging Access-Control-Allow-Origin
Walse
Walse

Posted on Originally published at apidog.com

Cara Mengatasi Error CORS: Debugging Access-Control-Allow-Origin

Panduan Praktis Mengatasi Kesalahan CORS: Preflight, Header, dan Debugging dengan Apidog

Anda meluncurkan frontend baru, membuka konsol, lalu melihat kesalahan CORS berwarna merah: permintaan “diblokir oleh kebijakan CORS.” API berjalan baik di Apidog atau curl, tetapi browser menolak memberikan respons kepada JavaScript Anda. Frustrasi? Ya. Misterius? Tidak, setelah mengetahui letak masalahnya.

Coba Apidog hari ini

Fakta penting yang sering terlewat: kesalahan CORS diberlakukan oleh browser, tetapi disebabkan oleh server. Browser memblokir respons karena server tidak mengirimkan header Access-Control-Allow-Origin yang benar. Jadi, perbaikannya hampir selalu berada di konfigurasi server, bukan di kode frontend.

Panduan ini membahas:

  • Apa itu CORS dan apa yang bukan
  • Cara kerja permintaan preflight
  • Enam kesalahan CORS umum beserta perbaikannya
  • Konfigurasi CORS untuk Express, Spring Boot, dan Nginx
  • Cara melakukan debugging dari luar browser

Apa itu kesalahan CORS?

CORS adalah singkatan dari Cross-Origin Resource Sharing. Secara default, browser menerapkan kebijakan same-origin: JavaScript yang berjalan di https://app.example.com tidak dapat membaca respons dari https://api.example.com karena skema, host, atau portnya berbeda.

CORS adalah mekanisme yang digunakan server untuk melonggarkan aturan tersebut secara sengaja. Lihat dokumentasi MDN CORS dan spesifikasi Fetch untuk detail lengkapnya.

Tiga hal berikut menjelaskan sebagian besar masalah CORS:

  • Browser yang menegakkan CORS. Panggilan antar-server, curl, dan klien API desktop tidak menerapkan pemeriksaan CORS.
  • Server yang mengonfigurasinya. Browser mengambil keputusan berdasarkan header respons dari server. Tanpa header yang sesuai, tidak ada akses.
  • Permintaan biasanya tetap mencapai server. Untuk permintaan sederhana, server memproses dan mengirim respons, tetapi browser menahan respons tersebut dari JavaScript.

CORS bukan tembok keamanan di sekitar API. CORS melindungi pengguna dari halaman berbahaya yang mencoba membaca data lintas-origin menggunakan cookie mereka.

Jadi, saat melihat kesalahan CORS, jangan langsung mencari solusi di frontend. Baca pesannya, lalu perbaiki header yang hilang atau salah di server.

Anatomi permintaan preflight

Untuk permintaan lintas-origin tertentu, browser terlebih dahulu mengirim permintaan OPTIONS yang disebut preflight. Ini biasanya terjadi ketika permintaan:

  • Menggunakan metode selain GET, HEAD, atau POST
  • Mengirim header kustom seperti Authorization
  • Menggunakan Content-Type: application/json

Contoh permintaan preflight:

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

Browser sedang bertanya: “Apakah halaman dari app.example.com boleh melakukan POST dengan header ini?”

Respons server yang benar:

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

Jika salah satu bagian hilang, browser membatalkan permintaan sebenarnya sebelum dikirim. Endpoint API tidak pernah berjalan; log hanya menunjukkan permintaan OPTIONS, sementara konsol menampilkan kesalahan CORS.

Access-Control-Max-Age memberi tahu browser berapa lama keputusan preflight dapat disimpan. Pada contoh di atas, browser menyimpannya selama 86.400 detik.

Saat melakukan debugging, selalu tanyakan:

Apakah preflight yang gagal, atau permintaan sebenarnya?

Enam kesalahan CORS paling umum

1. Header Access-Control-Allow-Origin tidak ada

Server mengirim respons tanpa header CORS. Browser tidak memiliki informasi untuk dievaluasi, sehingga akses diblokir.

Konfigurasikan server untuk mengizinkan origin tertentu:

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

Untuk API publik tanpa kredensial, Anda dapat menggunakan *:

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

Perhatikan bahwa respons error sering melewati header CORS. Misalnya, middleware menambahkan header pada respons 200, tetapi tidak pada 500. Akibatnya, konsol menampilkan kesalahan CORS alih-alih error server yang sebenarnya.

Pastikan header CORS juga dikirim pada respons 401, 403, dan 500, termasuk 403 Forbidden.

2. Wildcard * tidak dapat digunakan dengan kredensial

Pesan error biasanya berbunyi:

Nilai header Access-Control-Allow-Origin tidak boleh berupa wildcard * saat mode kredensial permintaan adalah include.

Frontend mengirim cookie atau header autentikasi dengan credentials: 'include', tetapi server merespons dengan:

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

Kombinasi ini dilarang oleh spesifikasi Fetch. Gunakan origin spesifik dan izinkan kredensial:

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

Validasi nilai Origin terhadap daftar izin sebelum mengembalikannya. Jangan memantulkan origin acak ketika kredensial diaktifkan.

3. Respons preflight gagal dalam pemeriksaan kontrol akses

Server tidak menangani permintaan OPTIONS. Kemungkinan penyebabnya:

  • Rute hanya mendefinisikan POST, sehingga OPTIONS menghasilkan 404 atau 405
  • Middleware autentikasi menolak preflight dengan 401
  • Preflight tidak membawa token autentikasi

Tangani OPTIONS sebelum autentikasi berjalan:

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

Dalam banyak framework, menempatkan middleware CORS di posisi paling awal sudah cukup.

4. Nilai header tidak sama dengan origin permintaan

Server mengirim Access-Control-Allow-Origin, tetapi nilainya salah. Penyebab umum:

  • Origin produksi di-hardcode saat menguji dari http://localhost:5173
  • Perbandingan http dan https tidak cocok
  • Ada garis miring akhir, misalnya https://app.example.com/

Origin harus dibandingkan secara persis:

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

Header Vary: Origin penting agar cache atau CDN tidak mengirim header untuk satu origin kepada origin lain.

5. Header permintaan atau metode tidak diizinkan

Dua pesan yang sering muncul:

Header authorization tidak diizinkan oleh Access-Control-Allow-Headers dalam respons preflight.

Metode PUT tidak diizinkan oleh Access-Control-Allow-Methods.

Preflight berhasil, tetapi responsnya tidak mencakup kebutuhan frontend. Perluas daftar metode dan header:

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

Nama header tidak peka huruf besar-kecil (case-insensitive). Nama metode peka huruf besar-kecil dan harus menggunakan huruf kapital.

6. Pengalihan tidak diizinkan untuk preflight

Preflight menerima respons 301 atau 302. Browser biasanya menolak mengikuti pengalihan selama preflight.

Penyebab umum:

  • URL http dialihkan ke https
  • Garis miring akhir ditambahkan oleh framework
  • Gateway mengalihkan /v1/orders ke /v1/orders/

Arahkan frontend langsung ke URL akhir. Gunakan https sejak awal, samakan konvensi garis miring router, lalu uji endpoint dengan permintaan OPTIONS manual. Pastikan responsnya 2xx, bukan 3xx.

Contoh konfigurasi server

Express

Gunakan middleware cors resmi daripada mengatur header secara manual:

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

Tempatkan middleware ini sebelum middleware autentikasi agar preflight tidak ditolak karena token yang hilang.

Pengguna Python dapat menerapkan pola serupa menggunakan Flask-CORS.

Spring Boot

Konfigurasikan CORS secara global melalui WebMvcConfigurer:

@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

Jika menggunakan Spring Security, panggil .cors(Customizer.withDefaults()) dalam rantai filter keamanan. Jika tidak, lapisan keamanan dapat memblokir preflight sebelum konfigurasi MVC memprosesnya.

Lihat dokumentasi Spring CORS untuk opsi lengkap.

Nginx

Jika Nginx berada di depan aplikasi, tangani preflight di edge:

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

Flag always penting. Tanpanya, Nginx dapat menghilangkan add_header pada respons 4xx dan 5xx, sehingga error CORS muncul kembali untuk setiap permintaan yang gagal.

Pilih satu lapisan untuk mengelola CORS. Jika Nginx dan aplikasi sama-sama menambahkan header, browser dapat menerima nilai duplikat seperti:

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

Respons tersebut akan ditolak.

Debug CORS di luar browser dengan Apidog

Pesan konsol hanya memberi tahu bahwa browser memblokir sesuatu. Pesan tersebut tidak menunjukkan apa yang sebenarnya dikirim server. Cara tercepat untuk melihatnya adalah mengeluarkan browser dari proses debugging.

Apidog adalah klien API desktop, sehingga permintaannya tidak tunduk pada pemeriksaan CORS browser. Ini menyediakan eksperimen yang bersih: kirim permintaan yang sama seperti yang dibuat frontend.

  • Jika berhasil di Apidog, logika API kemungkinan benar dan masalahnya ada pada header CORS.
  • Jika gagal di Apidog juga, Anda memiliki bug API biasa; gunakan teknik pengujian API umum.

Langkah debugging

  1. Ulangi permintaan sebenarnya. Salin permintaan yang gagal dari tab Network browser ke Apidog, termasuk metode, header, dan isi. Periksa status serta responsnya. Jika hasilnya 500, CORS bukan masalah utamanya.
  2. Uji preflight secara manual. Buat permintaan baru dengan metode OPTIONS, lalu tambahkan:
   Origin: https://app.example.com
   Access-Control-Request-Method: POST
   Access-Control-Request-Headers: authorization, content-type
Enter fullscreen mode Exit fullscreen mode
  1. Periksa header respons. Cari Access-Control-Allow-Origin, Access-Control-Allow-Methods, dan Access-Control-Allow-Headers. Bandingkan nilainya dengan kebutuhan frontend.
  2. Verifikasi perbaikan. Setelah mengubah konfigurasi server, kirim ulang permintaan OPTIONS yang sama dan pastikan header telah diperbarui.

Alur ini juga menyelesaikan masalah “berfungsi di klien API, tetapi gagal di browser”, seperti pada pengujian CORS Postman. Klien desktop berhasil karena melewati CORS; browser gagal karena server belum mengirim header yang diperlukan.

Anda dapat mengunduh Apidog secara gratis dan menyimpan permintaan OPTIONS di samping pengujian endpoint reguler.

Daftar periksa CORS 30 detik

Sebelum mengajukan bug, periksa hal-hal berikut:

  • Apakah respons yang gagal menyertakan Access-Control-Allow-Origin?
  • Apakah nilainya sama persis dengan origin halaman, termasuk skema, host, dan port, tanpa garis miring akhir?
  • Jika menggunakan cookie atau autentikasi, apakah origin spesifik digunakan bersama Access-Control-Allow-Credentials: true?
  • Apakah OPTIONS mengembalikan 2xx dengan metode dan header yang sesuai?
  • Apakah URL preflight mengalami pengalihan?
  • Apakah respons 401, 403, dan 500 membawa header CORS yang sama dengan respons sukses?

Sembilan dari sepuluh kali, jawabannya ada pada salah satu pemeriksaan tersebut. Verifikasi dengan permintaan OPTIONS manual di Apidog, perbaiki konfigurasi server, lalu lanjutkan pengembangan.

FAQ

Mengapa saya hanya mendapatkan kesalahan CORS di browser?

Karena hanya browser yang menegakkan CORS. Kebijakan same-origin melindungi pengguna dari halaman berbahaya yang membaca data terautentikasi mereka. curl, layanan backend, dan klien desktop tidak menerapkan aturan tersebut.

Jika permintaan berhasil di semua tempat kecuali browser, server kemungkinan kehilangan atau salah mengonfigurasi header CORS. API itu sendiri belum tentu bermasalah.

Apakah CORS berlaku untuk Postman atau Apidog?

Tidak. Postman dan Apidog adalah aplikasi desktop, bukan halaman web yang berjalan di dalam sandbox browser. Permintaan mereka melewati CORS sepenuhnya.

Inilah alasan keduanya berguna untuk debugging: Anda dapat melihat header respons mentah server tanpa penyaringan browser. Permintaan yang berhasil di klien desktop tidak membuktikan bahwa browser akan berhasil, tetapi membantu mengisolasi lapisan yang gagal.

Apakah kesalahan CORS merupakan fitur keamanan atau bug?

CORS adalah fitur keamanan. Kesalahan CORS berarti browser menolak mengekspos data lintas-origin kepada skrip, kecuali server mengizinkannya.

Menonaktifkan CORS melalui flag atau ekstensi browser hanya menyembunyikan gejala di komputer Anda. Pengguna lain tetap mengalami masalah yang sama. Perbaiki header server sebagai gantinya.

Dapatkah saya menggunakan Access-Control-Allow-Origin: * di mana-mana?

Hanya untuk API publik, hanya-baca, tanpa cookie atau autentikasi. Wildcard ditolak ketika kredensial disertakan dan dapat membuka data kepada setiap origin di web.

Untuk endpoint terautentikasi, gunakan daftar izin origin, kembalikan origin yang cocok, dan kirim Vary: Origin agar cache bersama memisahkan respons berdasarkan origin.

Top comments (0)