Fixing “CORS request did not succeed” in Firefox

You call fetch() against another origin, and Firefox’s console prints:

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the
remote resource at https://api.example.com/v1/data.
(Reason: CORS request did not succeed). Status code: (null).

The word “CORS” in the message misleads thousands of developers into editing Access-Control-Allow-Origin headers for hours. That is the wrong layer. This exact Firefox string — specifically the Status code: (null) clause — means no HTTP response ever reached the browser. There were no headers to check because there was no response.

This page is part of Decoding Browser CORS Error Messages, which maps each browser’s console strings to their true root cause; here we resolve this one Firefox message end to end.

Root Cause

The WHATWG Fetch Standard runs its CORS check (§4.9) only after a response is obtained. When the underlying fetch fails to obtain any response — the TLS handshake aborts, the connection is refused or times out, the request is blocked as mixed content, or the server never answers the preflight OPTIONS — the algorithm returns a network error before the CORS check runs. Firefox surfaces that pre-response network error with the generic reason CORS request did not succeed and Status code: (null), because a status code only exists once an HTTP response line has been received. This is categorically different from CORS header 'Access-Control-Allow-Origin' missing, which always carries a real numeric status because a response did arrive.

Prerequisite State

Before applying the fix, confirm:

The Four Transport Faults Behind This Message

Why no response arrives: four transport faults The browser attempts a request. Four faults can prevent any HTTP response from arriving: a TLS or certificate failure, a mixed-content block, a refused or timed-out connection, and a server that does not answer the OPTIONS preflight. All four short-circuit before the CORS check, producing Status code null. Firefox fetch() no response returns blocked 1. TLS / certificate failure untrusted, expired, or hostname mismatch 2. Mixed content block HTTP request from an HTTPS page 3. Connection refused / timed out wrong port, DNS, firewall, server down 4. No answer to OPTIONS preflight server drops or 5xx-es the preflight

Step-by-Step Fix

Step 1 — Confirm it really is a “no response” failure

Open the Firefox Web Console (F12) and read the full line. The signature is (Reason: CORS request did not succeed). Status code: (null). The (null) is the proof: no status line was received.

Step 2 — Test raw connectivity and TLS with curl

Run a verbose curl from the same machine and network:

curl -v https://api.example.com/v1/data

Watch the TLS handshake lines. If curl reports SSL certificate problem: unable to get local issuer certificate or certificate has expired, the browser is rejecting the certificate too — that is your fault. If curl completes only when you add -k, the certificate is untrusted and Firefox is correctly refusing it.

Step 3 — Fix the TLS chain

Serve the full certificate chain (leaf plus intermediates), not just the leaf. In Nginx:

server {
  listen 443 ssl;
  server_name api.example.com;

  ssl_certificate     /etc/ssl/certs/fullchain.pem;  # leaf + intermediates
  ssl_certificate_key /etc/ssl/private/privkey.pem;
}

A leaf-only ssl_certificate passes in curl (which may already trust the intermediate) but fails in Firefox, which requires the server to supply the chain.

Step 4 — Rule out mixed content

If the page is served over HTTPS, the request URL must also be HTTPS. Firefox blocks an http:// subresource request from an https:// document before it leaves the browser. Correct the URL:

// Blocked as mixed content on an HTTPS page:
fetch('http://api.example.com/v1/data');

// Correct:
fetch('https://api.example.com/v1/data');

Step 5 — Confirm the server answers OPTIONS

If the request is non-simple (custom headers, PUT/DELETE, or a JSON Content-Type), Firefox sends a preflight first. If the server drops or 5xx-es that OPTIONS, no response returns and you get this exact error. Verify:

curl -v -X OPTIONS https://api.example.com/v1/data \
  -H "Origin: https://app.example.com" \
  -H "Access-Control-Request-Method: PUT"

The server must answer with a 2xx status line. If curl hangs or the connection drops, add an OPTIONS handler — see OPTIONS endpoint design.

Verification

Re-run the request in Firefox with the Network panel open. A fixed transport produces a request row with a real status code (e.g. 200, 204, or even 403) instead of a red “blocked” row. At that point, if a CORS header problem remains, the message changes to name a header — which is progress, and a different fix.

curl one-liner to confirm a real response line now arrives:

curl -sS -o /dev/null -w "%{http_code}\n" https://api.example.com/v1/data

A numeric code (not 000) means the transport is healthy. curl’s 000 is the analogue of Firefox’s (null).

DevTools check:

Security Boundary Note

Do not “fix” a certificate error by having users add a security exception or by disabling certificate validation in the client. The CORS request did not succeed message for a TLS fault is the browser enforcing transport integrity — bypassing it exposes users to interception. Likewise, never work around a mixed-content block by downgrading the page to HTTP; serve the API over HTTPS instead. The correct remediation is always to make the transport genuinely valid, never to weaken the client’s trust checks.

Common Mistakes

Mistake Technical impact Fix
Editing Access-Control-Allow-Origin to fix Status code: (null) Hours lost; the header layer never runs because no response arrives Treat (null) as a transport fault; fix TLS/mixed-content/OPTIONS first
Shipping a leaf-only certificate Passes in curl, fails in Firefox with this exact error Serve fullchain.pem (leaf + intermediates)
Requesting http:// from an https:// page Firefox blocks as mixed content before sending Use an https:// request URL
Server drops or 5xx-es OPTIONS Preflight never returns; browser reports the failure against the real URL Add an OPTIONS handler returning 204 with CORS headers

FAQ

Why does Firefox say “Status code: (null)” instead of a real number?

Because no HTTP response line was ever received. A status code exists only once the server sends an HTTP response. When the TLS handshake fails, the connection is refused, or the request is blocked as mixed content, Firefox never gets a status line, so it reports (null). This distinguishes the failure from a header mismatch, which always has a real status code.

Does adding Access-Control-Allow-Origin fix “CORS request did not succeed”?

No. Response headers only matter once a response arrives. This error means no usable response arrived at all, so no header change on the server will resolve it. Fix the transport cause first — TLS certificate, mixed content, connection reachability, or a missing OPTIONS answer — then the CORS header check can even run.

Why does the same request work in curl but fail in Firefox with this error?

curl by default trusts a wider or differently configured CA set and does not enforce the browser’s mixed-content rule. A self-signed or untrusted certificate, or an HTTP subresource on an HTTPS page, will fail in Firefox but pass in curl. Add -v to curl and check whether you had to pass -k to bypass certificate validation — if so, the browser is correctly rejecting the same certificate.