CORS errors in the browser
Find the real cause behind a CORS error against api.danadresse.dk, which is open to every domain, and fix credentials, mixed content, proxies and key type.
The API allows calls from every domain. If you still get a CORS error in the browser console, the cause is almost always something other than a missing CORS permission: a call with cookies, an http:// address, a call that never reached the API (a network problem, a firewall, a browser extension), or a proxy that changes the headers. This guide goes through the causes in the order they usually turn up.
What the API sends
Every response from the API carries Access-Control-Allow-Origin: *, errors included: 400, 401, 403 (also from the abuse filter), 404, 429, 500 and 503. Responses to a call with an Origin header also list the headers your code may read:
| Header | Value |
|---|---|
Access-Control-Allow-Origin | * |
Access-Control-Expose-Headers | X-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 |
A preflight (OPTIONS) is answered with Access-Control-Allow-Methods: GET, POST, OPTIONS, the headers you ask for (for example x-api-key and content-type), and Access-Control-Max-Age: 86400. A preflight needs no key and does not count as a call.
See for yourself. The first call is a plain GET with an Origin header, the second a preflight:
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 in the network tab
Open the developer tools' network tab and find the call:
- A status such as 400, 401, 403, 404, 429 or 500 is visible. Then it is not a CORS problem. Click the call and read the response. See The error format.
- Status 410 from
dawa.aws.dkorapi.dataforsyningen.dk. The code still calls DAWA, which closed on 1 October 2026. Search the whole code base, bundled files and CMS settings included, and switch tohttps://api.danadresse.dk. See Switch from DAWA. - The call shows as
(failed),CORS errororNS_ERROR_…with no status. The response did not arrive with CORS headers. Go through the checklist.
The checklist
1. The call sends cookies (credentials).
- Symptom: The console says something like
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'. - Cause:
fetch(url, { credentials: "include" }),axioswithwithCredentials: trueor jQuery withxhrFields: { withCredentials: true }. Browsers reject*together with credentials, and the API does not use cookies. - Fix: Remove credentials from the call. The key goes in
X-Api-Keyorapi_key.
const API_KEY = "YOUR_BROWSER_KEY"; // locked to your domain // Wrong: credentials: "include" causes a CORS error // Right: const res = await fetch("https://api.danadresse.dk/autocomplete?q=Vesterbrogade", { headers: { "X-Api-Key": API_KEY }, });
2. Mixed content: http:// from an https:// page.
- Symptom: The console says
Mixed Content, or the network tab showsblocked:mixed-content. - Cause: The API address in the code starts with
http://. The browser blocks the call before it is sent. - Fix: Use
https://api.danadresse.dk.
3. The call never reached the API.
- Cause: Every answer from the API itself carries CORS headers, its errors included. A CORS error with no status therefore usually means the answer came from somewhere else, or never came:
- an ad blocker or privacy extension in the browser that blocks
api.danadresse.dk - a corporate firewall, proxy or VPN that cuts or rewrites the call
- a 502 or 504 from the proxy in front of the API, for example on a timeout or during a deployment
- a lost connection (offline, DNS)
- an ad blocker or privacy extension in the browser that blocks
- Fix: Try a private window without extensions, then run the same call with curl from the same network. If curl gets a response with
access-control-allow-origin, the problem is in the browser or the network. If you get 502 or 504, see Error 503 and timeouts.
4. Your own proxy changes the headers.
- Symptom: The console says
The 'Access-Control-Allow-Origin' header contains multiple values '*, *'. - Cause: You call the API through your own server (nginx, a Next.js route, a Cloudflare Worker), and the proxy both forwards our
Access-Control-Allow-Originand adds its own. - Fix: Let only one hop set CORS headers. If your page calls your own proxy on the same domain, you need no CORS headers at all. See Next.js route handler as a proxy.
// Next.js route handler: only pass on the body and content-type 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. Old JSONP code.
- How it works:
$.ajax({ dataType: "jsonp" })and<script>tags withcallback=work on the DAWA paths. The answer is wrapped asfn(…);withContent-Type: application/javascript, and JSONP needs no CORS at all. A script tag cannot send headers, so the key goes in the URL asapi_key=. - If it still fails: the callback name must be a plain JavaScript name such as
cborjQuery123_456(otherwise you get plain JSON back),/api/v1does not answer JSONP, and a Content Security Policy on your page must allowhttps://api.danadresse.dkinscript-src. - Fix: For new code, use
fetch. See Formats.
Things that look like CORS errors but are not
A header is null in JavaScript. The browser lets code read the headers in Access-Control-Expose-Headers (see the table above) and the standard headers that are always allowed. So res.headers.get("Retry-After") and the X-RateLimit-* and X-Quota-* headers work in the browser. Other headers, for example X-Danadresse-Key-Status, are sent but return null in browser code. Read them on the server.
403 origin_not_allowed. The key is locked to domains and the page is not on the list. The response has CORS headers and a readable body. See Error 403.
The server key works in the browser. A key without a domain lock works from any page, because CORS is open. That is not a security boundary: anyone can copy the key from the page source. Use a browser key with Allowed domains in frontend code, and keep the server key on the server. See Browser keys and allowed domains.
Next steps
Related guides
Markdown source Spotted an error? Tell us API-reference (Swagger)