Missing Access-Control-Allow-Headers

Missing Access-Control-Allow-Headers

What Does “Missing Access-Control-Allow-Headers” Mean?

The Access-Control-Allow-Headers header is used in CORS (Cross-Origin Resource Sharing) to specify which HTTP headers are allowed in cross-origin requests.

This error occurs when:

  • The client sends custom headers e.g.: Authorization Content-Type but the server doesn’t explicitly allow them.
  • The preflight request (OPTIONS method) does not return Access-Control-Allow-Headers.
  • The API gateway or reverse proxy blocks headers due to security settings.

Common Causes & How to Fix Them

1. Missing Access-Control-Allow-Headers in Preflight Response

🔍 Issue:
If a request includes custom headers like Authorization, X-Requested-With, or Content-Type, the browser sends a preflight request (OPTIONS method).

If the server does not respond with Access-Control-Allow-Headers, the request fails.

Fix:
Set Access-Control-Allow-Headers in the response:

For Express.js (Node.js):

app.use((req, res, next) => {
  res.header("Access-Control-Allow-Origin", "https://yourdomain.com");
  res.header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
  res.header("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Requested-With");
  next();
});

For Flask (Python):

@app.after_request
def add_cors_headers(response):
    response.headers['Access-Control-Allow-Origin'] = 'https://yourdomain.com'
    response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, OPTIONS'
    response.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization, X-Requested-With'
    return response

2. Handling Preflight (OPTIONS) Requests

🔍 Issue:
When a POST, PUT, or DELETE request includes custom headers, the browser first sends an OPTIONS request to check if the server allows it.

If the server does not respond to OPTIONS, the request fails.

Fix:
Ensure the server correctly responds to OPTIONS requests:

For Express.js:

app.options("*", (req, res) => {
  res.header("Access-Control-Allow-Origin", "https://yourdomain.com");
  res.header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
  res.header("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Requested-With");
  res.send();
});

For Flask:

@app.route('/your-endpoint', methods=['OPTIONS'])
def handle_options():
    response = jsonify()
    response.headers['Access-Control-Allow-Origin'] = 'https://yourdomain.com'
    response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, OPTIONS'
    response.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization, X-Requested-With'
    return response

3. Client Request Must Include Allowed Headers

🔍 Issue:
If the client sends headers that aren’t listed in Access-Control-Allow-Headers, the browser will block the request.

Fix:
Ensure the client request matches the allowed headers:

Fetch API:

fetch("https://yourdomain.com/api", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Authorization": "Bearer YOUR_TOKEN",
  },
  body: JSON.stringify({ key: "value" }),
});

Axios:

axios.post("https://yourdomain.com/api", 
  { key: "value" }, 
  { headers: { "Content-Type": "application/json", "Authorization": "Bearer YOUR_TOKEN" } }
);

4. Allowing Multiple Headers Dynamically

🔍 Issue:
If your API supports multiple frontends, manually listing allowed headers may not scale well.

Fix:
Dynamically set headers using Vary: Access-Control-Request-Headers:

For Express.js:

app.use((req, res, next) => {
  res.header("Access-Control-Allow-Origin", req.headers.origin || "*");
  res.header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
  res.header("Access-Control-Allow-Headers", req.headers["access-control-request-headers"] || "Content-Type, Authorization");
  res.header("Vary", "Access-Control-Request-Headers");
  next();
});

For Flask:

@app.after_request
def add_cors_headers(response):
    request_headers = request.headers.get("Access-Control-Request-Headers")
    if request_headers:
        response.headers["Access-Control-Allow-Headers"] = request_headers
        response.headers["Vary"] = "Access-Control-Request-Headers"
    return response

Conclusion

The “Missing Access-Control-Allow-Headers” error is usually caused by:
✅ The server not returning Access-Control-Allow-Headers in preflight requests.
✅ The client sending custom headers that the server has not explicitly allowed.
✅ The preflight request (OPTIONS) missing a response.