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

Mengalami error CORS? Pelajari apa yang memicunya, bagaimana preflight bekerja, 6 kegagalan Access-Control-Allow-Origin yang paling umum, dan perbaikan yang tepat untuk masing-masing.

Ashley Innocent

Ashley Innocent

31 August 2026

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

Apidog untuk Perusahaan

Penerapan On-Premises

SSO & RBAC

Sesuai SOC 2

Jelajahi Apidog Enterprise

Anda meluncurkan frontend baru, membuka konsol, dan di sana muncul: kesalahan CORS berwarna merah yang menyatakan permintaan "diblokir oleh kebijakan CORS." API Anda berfungsi dengan baik di Apidog atau curl, namun browser menolak untuk memberikan respons kepada JavaScript Anda. Frustrasi? Ya. Misterius? Tidak, setelah Anda tahu di mana letak kesalahannya.

Berikut adalah fakta inti yang sering disembunyikan oleh sebagian besar tutorial: kesalahan CORS diberlakukan oleh browser tetapi disebabkan oleh server. Browser memblokir respons karena server Anda tidak mengirimkan header Access-Control-Allow-Origin yang benar. Jadi, perbaikan hampir selalu terjadi pada konfigurasi server, bukan pada kode frontend Anda.

Panduan ini akan membahas apa itu CORS, bagaimana cara kerja permintaan preflight, enam pesan kesalahan CORS paling umum beserta perbaikan tepat untuk masing-masing, dan konfigurasi yang berfungsi untuk Express, Spring Boot, dan Nginx. Anda juga akan melihat cara melakukan debug dari luar browser, yang merupakan cara tercepat untuk membedakan antara "server salah konfigurasi" dan "browser memblokir."

Apa itu kesalahan CORS (dan apa yang bukan)

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 sengaja melonggarkan aturan ini. Detail lengkapnya ada di dokumentasi MDN CORS, dan algoritma dasarnya didefinisikan dalam spesifikasi Fetch.

Tiga poin berikut dapat menghilangkan sebagian besar kebingungan:

Jadi, ketika Anda melihat kesalahan CORS, jangan mencari solusi di frontend. Baca pesan kesalahannya, lalu perbaiki header yang hilang atau salah di server.

Anatomi permintaan preflight

Sebelum permintaan lintas-origin tertentu, browser mengirimkan 'pemandu': permintaan OPTIONS yang disebut preflight. Ini terjadi ketika permintaan Anda menggunakan metode selain GET, HEAD, atau POST, mengirimkan header kustom seperti Authorization, atau menggunakan Content-Type seperti application/json.

Permintaan preflight terlihat seperti ini:

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

Browser bertanya: "Sebuah halaman di app.example.com ingin melakukan POST di sini dengan header ini. Diizinkan?" Jawaban 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

Jika ada bagian yang hilang, browser membatalkan permintaan sebenarnya sebelum permintaan itu sempat dikirim. Endpoint API Anda tidak pernah berjalan, log Anda hanya menunjukkan adanya permintaan OPTIONS, dan konsol menunjukkan kesalahan CORS. Access-Control-Max-Age memberitahu browser untuk menyimpan putusan ini (86400 detik di sini), sehingga permintaan berulang melewati preflight.

Ingatlah proses dua langkah ini. Separuh dari semua debugging CORS bermuara pada satu pertanyaan: apakah preflight gagal, atau apakah permintaan sebenarnya yang gagal?

6 kesalahan CORS paling umum dan cara memperbaikinya

Browser menulis pesan kesalahan CORS yang mengejutkan presisinya. Cocokkan pesan Anda dengan daftar di bawah ini.

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

Ini adalah kasus klasik. Server Anda mengirim respons tanpa header CORS sama sekali. Browser tidak memiliki apa pun untuk dievaluasi, sehingga memblokir akses.

Perbaikan: Konfigurasikan server untuk mengirim Access-Control-Allow-Origin baik dengan origin pemohon spesifik atau * untuk API publik tanpa kredensial:

Access-Control-Allow-Origin: https://app.example.com

Satu jebakan: respons kesalahan seringkali melewati header CORS meskipun respons sukses menyertakannya. Jika API Anda mengembalikan 500 dan middleware hanya mendekorasi 200, konsol akan menampilkan kesalahan CORS alih-alih kesalahan server yang sebenarnya. Pastikan header CORS dilampirkan ke setiap respons, termasuk halaman 403 Forbidden dan 500.

2. Wildcard ‘*’ tidak dapat digunakan dengan kredensial

Pesan berbunyi: "Nilai header 'Access-Control-Allow-Origin' tidak boleh berupa wildcard '*' saat mode kredensial permintaan adalah 'include'."

Frontend Anda mengirimkan cookie atau header otentikasi dengan credentials: 'include', tetapi server menjawab dengan Access-Control-Allow-Origin: *. Spesifikasi Fetch melarang pasangan ini; wildcard ditambah kredensial akan memungkinkan situs mana pun di internet untuk membaca respons yang diautentikasi.

Perbaikan: Eko origin yang tepat alih-alih wildcard, dan tambahkan header kredensial:

Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Credentials: true

Validasi Origin yang masuk terhadap daftar izin sebelum mengulanginya. Memantulkan origin acak dengan kredensial yang diaktifkan menggagalkan seluruh perlindungan.

3. Respons terhadap permintaan preflight tidak lolos pemeriksaan kontrol akses

Server Anda tidak pernah menangani permintaan OPTIONS. Mungkin rute hanya mendefinisikan POST, sehingga OPTIONS mengembalikan 404 atau 405. Mungkin middleware otentikasi menolaknya dengan 401 karena preflight tidak membawa token (browser tidak pernah melampirkan kredensial ke preflight).

Perbaikan: Tangani OPTIONS secara eksplisit dan kembalikan 2xx dengan set lengkap header CORS sebelum otentikasi berjalan. Di sebagian besar kerangka kerja, menempatkan middleware CORS terlebih dahulu akan menyelesaikannya. Jika Anda menulisnya secara manual:

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);
});

4. Nilai header tidak sama dengan origin yang diberikan

Server mengirim header Access-Control-Allow-Origin, tetapi menyebutkan origin yang salah. Penyebab umum: origin produksi yang di-hardcode saat Anda menguji dari http://localhost:5173, perbandingan daftar izin gagal pada http vs https, atau garis miring (slash) akhir yang nyasar (https://app.example.com/ bukanlah nilai origin yang valid).

Perbaikan: Bandingkan header Origin permintaan dengan daftar izin Anda secara persis, ulangi kecocokannya, dan kirim Vary: Origin agar cache dan CDN tidak melayani header satu origin ke origin lainnya:

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');
}

5. Bidang header permintaan atau metode tidak diizinkan

Dua pesan yang serupa: "Bidang header permintaan authorization tidak diizinkan oleh Access-Control-Allow-Headers dalam respons preflight" dan "Metode PUT tidak diizinkan oleh Access-Control-Allow-Methods."

Preflight berhasil, tetapi jawabannya tidak mencakup apa yang dibutuhkan permintaan Anda. Anda menambahkan header Authorization atau X-Request-Id, dan daftar izin server tidak pernah menyebutkannya.

Perbaikan: Perpanjang respons preflight untuk menyertakan setiap header dan metode yang dikirim frontend Anda:

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

Nama header di sini tidak peka huruf besar/kecil (case-insensitive). Metode peka huruf besar/kecil (case-sensitive) dan menggunakan huruf kapital.

6. Pengalihan tidak diizinkan untuk permintaan preflight

Preflight mengenai URL yang mengembalikan 301 atau 302, dan browser menolak untuk mengikuti pengalihan selama preflight. Pelaku umum: URL http yang mengalihkan ke https, garis miring akhir yang hilang yang "membantu" dialihkan oleh framework Anda, atau gateway yang memantulkan /v1/orders ke /v1/orders/.

Perbaikan: Arahkan frontend Anda langsung ke URL akhir. Gunakan https sejak awal, sesuaikan konvensi garis miring akhir router Anda, dan konfirmasikan dengan panggilan OPTIONS manual untuk memeriksa apakah endpoint menjawab dengan 2xx alih-alih 3xx.

Contoh konfigurasi server

Berikut adalah pengaturan CORS yang benar dalam tiga tumpukan umum.

Express

Gunakan middleware cors resmi alih-alih membuat 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
}));

Tempatkan middleware ini sebelum middleware otentikasi Anda agar preflight tidak pernah ditolak karena token hilang. Pengembang Python mendapatkan pola yang sama dari ekstensi Flask-CORS, yang membungkus logika header yang identik untuk aplikasi Flask.

Spring Boot

Konfigurasi 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);
    }
}

Menggunakan Spring Security? Panggil .cors(Customizer.withDefaults()) di rantai filter keamanan Anda juga, atau lapisan keamanan akan memblokir preflight sebelum konfigurasi MVC melihatnya. Lihat dokumentasi Spring CORS untuk opsi lengkap.

Nginx

Ketika Nginx mengakhiri permintaan di depan aplikasi Anda, jawab preflight di tepi:

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;
}

Flag always itu penting. Tanpanya, Nginx akan menghilangkan direktif add_header pada respons 4xx dan 5xx, yang menciptakan kembali kesalahan nomor satu pada setiap permintaan yang gagal. Dan pilih satu lapisan untuk mengelola CORS: jika Nginx dan aplikasi Anda menambahkan header, browser akan melihat duplikat seperti Access-Control-Allow-Origin: *, * dan menolak respons.

Debug CORS di luar browser dengan Apidog

Kesalahan konsol memberitahu Anda bahwa browser memblokir sesuatu. Itu tidak memberitahu Anda apa yang dikirim server. Cara tercepat untuk melihat kebenarannya adalah dengan mengeluarkan browser dari loop.

Apidog adalah klien API desktop, sehingga permintaannya sama sekali tidak tunduk pada pemeriksaan CORS browser. Itu memberi Anda eksperimen yang bersih: kirim permintaan yang sama dari Apidog yang dibuat oleh frontend Anda. Jika berhasil di sana, logika API Anda baik-baik saja dan masalahnya murni karena header CORS yang hilang. Jika gagal di sana juga, Anda memiliki bug API biasa yang mengenakan kostum CORS, dan teknik pengujian API umum berlaku.

Sesi debugging CORS di Apidog terlihat seperti ini:

  1. Ulangi permintaan yang sebenarnya. Salin permintaan yang gagal dari tab Jaringan browser Anda dan buat ulang di Apidog dengan metode, header, dan isi yang sama. Periksa status dan isi. Status 500 di sini berarti CORS bukanlah masalah Anda.
  2. Uji preflight secara manual. Buat permintaan baru, atur metode ke OPTIONS, dan tambahkan header yang akan dikirim browser: Origin: https://app.example.com, Access-Control-Request-Method: POST, dan Access-Control-Request-Headers: authorization, content-type. Kirimkan.
  3. Periksa header respons. Di panel respons, cari Access-Control-Allow-Origin, Access-Control-Allow-Methods, dan Access-Control-Allow-Headers. Bandingkan setiap nilai dengan apa yang dibutuhkan frontend Anda. Header yang hilang, origin yang salah, atau status 3xx langsung terlihat, tidak perlu menebak-nebak di konsol.
  4. Verifikasi perbaikan. Setelah mengubah konfigurasi server, kirim ulang permintaan OPTIONS yang sama yang disimpan dan lihat header diperbarui. Tidak perlu menyebarkan frontend, tidak ada ritual membersihkan cache.

Alur kerja ini juga menyelesaikan argumen abadi "berfungsi di klien API saya, gagal di browser" dalam hitungan detik, teka-teki yang sama di balik pertanyaan uji CORS Postman. Klien berfungsi karena melewati CORS. Browser gagal karena server Anda belum mengucapkan kata-kata ajaib. Unduh Apidog secara gratis dan simpan permintaan OPTIONS di samping tes endpoint reguler Anda; masalah CORS di masa depan akan padam dalam satu klik.

Daftar periksa CORS 30 detik

Sebelum Anda mengajukan bug, periksa daftar ini:

Sembilan dari sepuluh kali, salah satu dari enam baris itu adalah jawaban Anda. Verifikasi dengan permintaan OPTIONS manual di Apidog, perbaiki konfigurasi server, dan kembali membangun.

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 terotentikasi mereka, jadi browser memeriksa Access-Control-Allow-Origin pada setiap respons lintas-origin. curl, layanan backend, dan klien desktop tidak memiliki aturan tersebut. Jika permintaan berhasil di mana-mana kecuali browser, server Anda kehilangan atau salah mengonfigurasi header CORS; API itu sendiri sehat.

Apakah CORS berlaku untuk Postman atau Apidog?

Tidak. Postman dan Apidog adalah aplikasi desktop, bukan halaman web yang berjalan di dalam sandbox browser, jadi permintaan mereka melewati CORS sepenuhnya. Itulah yang membuat mereka berguna untuk debugging CORS: mereka menunjukkan kepada Anda header respons mentah server tanpa penyaringan browser. Kebingungan uji CORS Postman biasanya dimulai di sini; permintaan yang berhasil di klien desktop tidak membuktikan apa pun tentang perilaku browser, tetapi mengisolasi lapisan yang gagal.

Apakah kesalahan CORS adalah fitur keamanan atau bug?

Fitur. Kesalahan CORS berarti browser melakukan tugasnya: menolak untuk mengekspos data respons lintas-origin ke skrip kecuali server memilih untuk ikut serta. Menonaktifkan CORS di browser dengan flag atau ekstensi menyembunyikan gejala pada mesin Anda sementara setiap pengguna masih menghadapi 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 otentikasi. Wildcard ditolak kapan pun kredensial disertakan, dan itu mengumumkan bahwa data Anda terbuka untuk setiap origin di web. Untuk apa pun yang diautentikasi, pertahankan daftar izin origin, ulangi origin yang cocok, dan kirim Vary: Origin agar cache bersama menjaga respons tetap terpisah.

Mengembangkan API dengan Apidog

Apidog adalah alat pengembangan API yang membantu Anda mengembangkan API dengan lebih mudah dan efisien.