GuidesHEADERS

Access-Control-Allow-Headers

The Access-Control-Allow-Headers response header appears on preflight responses and names which HTTP headers the actual cross-origin request may carry. When browser code adds custom or non-safelisted headers, the browser asks the server for permission first. If the answer omits any requested header, the browser stops the request before it ever fires.

What the header does

Access-Control-Allow-Headers is an HTTP response header sent only on preflight responses. It tells the browser which request headers the upcoming cross-origin request is allowed to carry.

It is required whenever the preflight includes an Access-Control-Request-Headers header, which carries a sorted, lowercase, comma-separated list of the unique non-safelisted headers the client intends to send.

How the exchange works

When script initiates a cross-origin request with non-safelisted headers, the browser intercepts the call and sends an OPTIONS preflight. The preflight advertises the required headers in Access-Control-Request-Headers.

The browser asks, the server answers
> OPTIONS /data HTTP/2
> origin: https://app.example.com
> access-control-request-method: POST
> access-control-request-headers: authorization, content-type, x-api-key

< HTTP/2 204
< access-control-allow-origin: https://app.example.com
< access-control-allow-methods: POST
< access-control-allow-headers: Authorization, Content-Type, X-Api-Key

# Matching is case-insensitive, but every requested header must be covered.
# One missing name fails the preflight and the POST never fires.

The server answers with Access-Control-Allow-Headers naming the permitted headers. If any requested header is missing from that list, the browser blocks the call with a "not allowed by Access-Control-Allow-Headers" error. The preflight guide walks through the full failure flow.

How the browser matches headers

The browser checks every header the actual request will send against the preflight response before dispatching anything.

  • Header name matching is case-insensitive: Content-Type, content-type, and CONTENT-TYPE are the same header.
  • Every non-safelisted header on the actual request must be covered by the preflight response.
  • A single missing name fails the whole preflight.
  • When the preflight fails, the actual request is never sent to the server.

Listing custom headers

Custom headers, and standard headers with non-safelisted values, must be named explicitly. That includes X-Api-Key, Authorization, and Content-Type when its value is application/json.

Name every custom header explicitly
# The actual request sends an API key and a JSON body
Access-Control-Allow-Headers: X-Api-Key, Content-Type

# The wildcard only works for requests without credentials, and it never
# covers Authorization: name it explicitly
Access-Control-Allow-Headers: Authorization, X-Api-Key, Content-Type

Because application/json falls outside the safelist, any cross-origin JSON POST, PUT, or PATCH triggers a preflight and fails unless Content-Type appears in Access-Control-Allow-Headers.

The wildcard and the Authorization trap

Access-Control-Allow-Headers accepts an asterisk wildcard, but the wildcard has strict limits around credentials and authentication.

  • The wildcard counts as a real wildcard only on requests without credentials such as cookies or HTTP authentication.
  • On a credentialed request the browser treats * as the literal header name *, with no wildcard meaning.
  • The Authorization header is never covered by the wildcard and must always be listed by name.

Safelisted headers need no listing

CORS-safelisted request headers are always allowed and never need to appear in Access-Control-Allow-Headers.

  • The safelist covers Accept, Accept-Language, and Content-Language.
  • Content-Type is safelisted only for application/x-www-form-urlencoded, multipart/form-data, and text/plain.
  • Range is safelisted only with a single range value.
  • Listing a safelisted header anyway, such as Access-Control-Allow-Headers: Accept, is legal and lifts the additional safelist restrictions on that header.

Good questions.

Is Access-Control-Allow-Headers case-sensitive?

No. Header name matching against the list is case-insensitive, so the server can emit names in any casing.

Why does Content-Type need listing for JSON requests?

Content-Type is only CORS-safelisted for application/x-www-form-urlencoded, multipart/form-data, and text/plain. A value of application/json falls outside the safelist and requires explicit allowance.

Does the wildcard cover Authorization?

No. The Authorization header is never covered by the wildcard in Access-Control-Allow-Headers and must always be named explicitly.

Can safelisted headers be listed anyway?

Yes. Listing a safelisted header such as Accept is valid and lifts the extra restrictions the safelist places on that header.