CORS-fejl i browseren

Find den rigtige årsag bag en CORS-fejl mod api.danadresse.dk, som er åbent for alle domæner, og ret credentials, mixed content, proxyer og nøgletype.

5 min Niveau: Øvet ✓ Kode testet

API'et tillader kald fra alle domæner. Får du alligevel en CORS-fejl i browserkonsollen, er årsagen næsten altid en anden end en manglende CORS-tilladelse: et kald med cookies, en http://-adresse, et kald, der aldrig nåede frem til API'et (et netværksproblem, en firewall, en browserudvidelse), eller en proxy, der ændrer headerne. Denne guide går årsagerne igennem i den rækkefølge, de typisk opstår.

Hvad API'et sender

Alle svar fra API'et har Access-Control-Allow-Origin: *, også fejlene: 400, 401, 403 (også fra misbrugsfilteret), 404, 429, 500 og 503. Svar på et kald med en Origin-header lister også de headere, din kode må læse:

HeaderVærdi
Access-Control-Allow-Origin*
Access-Control-Expose-HeadersX-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-RateLimit-Minute-Limit, X-RateLimit-Minute-Remaining, X-RateLimit-Minute-Reset, Retry-After, X-Quota-Grace, X-Quota-Remaining, ETag

Et preflight (OPTIONS) besvares med Access-Control-Allow-Methods: GET, POST, OPTIONS, de headere du beder om (fx x-api-key og content-type), og Access-Control-Max-Age: 86400. Preflight kræver ingen nøgle og tæller ikke som et kald.

Se det selv. Det første kald er et almindeligt GET med en Origin-header, det andet et preflight:

Shell#cors-headers
curl -s -D - -o /dev/null "https://api.danadresse.dk/postnumre/8000" \
  -H "Origin: https://shop.example.dk" \
  -H "X-Api-Key: $DANADRESSE_API_KEY" | tr 'A-Z' 'a-z' | grep '^access-control'

curl -s -D - -o /dev/null -X OPTIONS "https://api.danadresse.dk/postnumre/8000" \
  -H "Origin: https://shop.example.dk" \
  -H "Access-Control-Request-Method: GET" \
  -H "Access-Control-Request-Headers: x-api-key" | tr 'A-Z' 'a-z' | grep '^access-control'

Start i netværksfanen

Åbn udviklerværktøjernes netværksfane og find kaldet:

  • En status som 400, 401, 403, 404, 429 eller 500 er synlig. Så er det ikke et CORS-problem. Klik på kaldet og læs svaret. Se Fejlformatet.
  • Status 410 fra dawa.aws.dk eller api.dataforsyningen.dk. Koden kalder stadig DAWA, som lukkede 1. oktober 2026. Søg hele kodebasen igennem, også bundlede filer og CMS-indstillinger, og skift til https://api.danadresse.dk. Se Skift fra DAWA.
  • Kaldet står som (failed), CORS error eller NS_ERROR_… uden status. Så nåede svaret ikke frem med CORS-headere. Gå tjeklisten igennem.

Tjeklisten

1. Kaldet sender cookies (credentials).

  • Symptom: Konsollen skriver noget i retning af The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'.
  • Årsag: fetch(url, { credentials: "include" }), axios med withCredentials: true eller jQuery med xhrFields: { withCredentials: true }. Browsere afviser * sammen med credentials, og API'et bruger ikke cookies.
  • Løsning: Fjern credentials fra kaldet. Nøglen skal i X-Api-Key eller api_key.
JavaScript#no-credentials
const API_KEY = "DIN_BROWSER_NØGLE"; // låst til dit domæne

// Forkert: credentials: "include" giver en CORS-fejl
// Rigtigt:
const res = await fetch("https://api.danadresse.dk/autocomplete?q=Vesterbrogade", {
  headers: { "X-Api-Key": API_KEY },
});

2. Mixed content: http:// fra en https://-side.

  • Symptom: Konsollen skriver Mixed Content, eller netværksfanen viser blocked:mixed-content.
  • Årsag: API-adressen i koden starter med http://. Browseren blokerer kaldet, før det bliver sendt.
  • Løsning: Brug https://api.danadresse.dk.

3. Kaldet nåede aldrig frem til API'et.

  • Årsag: Alle svar fra selve API'et har CORS-headere, også fejlene. En CORS-fejl uden status betyder derfor som regel, at svaret kom et andet sted fra, eller slet ikke kom:
    • en annonceblokker eller privatlivsudvidelse i browseren, der blokerer api.danadresse.dk
    • en firmafirewall, proxy eller et VPN, der afbryder eller ændrer kaldet
    • en 502 eller 504 fra proxyen foran API'et, fx ved en timeout eller under en udrulning
    • en tabt forbindelse (offline, DNS)
  • Løsning: Prøv et privat vindue uden udvidelser, og kør derefter det samme kald med curl fra samme netværk. Får curl et svar med access-control-allow-origin, ligger problemet i browseren eller netværket. Får du 502 eller 504, så se Fejl 503 og timeouts.

4. Din egen proxy ændrer headerne.

  • Symptom: Konsollen skriver The 'Access-Control-Allow-Origin' header contains multiple values '*, *'.
  • Årsag: Du kalder API'et gennem din egen server (nginx, en Next.js-route, en Cloudflare Worker), og proxyen både sender vores Access-Control-Allow-Origin videre og tilføjer sin egen.
  • Løsning: Lad kun ét led sætte CORS-headere. Kalder din side din egen proxy på samme domæne, behøver du slet ingen CORS-headere. Se Next.js route handler som proxy.
JavaScript#proxy-strip-headers
// Next.js route handler: send kun body og content-type videre
const upstream = await fetch(url, { headers: { "X-Api-Key": process.env.DANADRESSE_API_KEY } });
return new Response(upstream.body, {
  status: upstream.status,
  headers: { "content-type": upstream.headers.get("content-type") ?? "application/json" },
});

5. Gammel JSONP-kode.

  • Sådan virker det: $.ajax({ dataType: "jsonp" }) og <script>-tags med callback= virker på DAWA-stierne. Svaret pakkes ind som fn(…); med Content-Type: application/javascript, og JSONP kræver slet ikke CORS. Et script-tag kan ikke sende headere, så nøglen skal i URL'en som api_key=.
  • Fejler det alligevel: callback-navnet skal være et almindeligt JavaScript-navn som cb eller jQuery123_456 (ellers får du almindelig JSON tilbage), /api/v1 svarer ikke med JSONP, og en Content Security Policy på din side skal tillade https://api.danadresse.dk i script-src.
  • Løsning: Brug fetch i ny kode. Se Formater.

Ting, der ligner CORS-fejl, men ikke er det

En header er null i JavaScript. Browseren lader koden læse headerne i Access-Control-Expose-Headers (se tabellen ovenfor) og de standardheadere, der altid er tilladt. Så res.headers.get("Retry-After") og headerne X-RateLimit-* og X-Quota-* virker i browseren. Andre headere, fx X-Danadresse-Key-Status, bliver sendt, men giver null i browserkode. Læs dem på serveren.

403 origin_not_allowed. Nøglen er låst til domæner, og siden står ikke på listen. Svaret har CORS-headere og en læsbar body. Se Fejl 403.

Servernøglen virker i browseren. En nøgle uden domænelås virker fra enhver side, fordi CORS er åbent. Det er ikke en sikkerhedsgrænse: enhver kan kopiere nøglen fra sidens kildekode. Brug en browsernøgle med Tilladte domæner i frontend-kode, og hold servernøglen på serveren. Se Browsernøgler og tilladte domæner.

Næste skridt

Relaterede guides

Markdown-kilde Fejl i guiden? Skriv til os API-reference (Swagger)

Prøv det med en gratis nøgle

2 000 kald/måned på gratisplanen. Intet kreditkort.

Opret konto →