Misconfigured Access-Control-Allow-Credentials

Misconfigured Access-Control-Allow-Credentials

What Does “Misconfigured Access-Control-Allow-Credentials” Mean?

The "Access-Control-Allow-Credentials" header is used in Cross-Origin Resource Sharing (CORS) to allow the browser to send credentials (such as cookies, Authorization headers, or TLS client certificates) with cross-origin requests.

A misconfiguration occurs when:

  • The server sets Access-Control-Allow-Credentials: true but does not properly configure CORS headers.
  • The Access-Control-Allow-Origin header is wildcard * which is not allowed with credentials.
  • The browser blocks credentials due to incorrect request headers.

Common Causes & How to Fix Them

1. Using * (Wildcard) with Credentials – NOT ALLOWED

Issue:
The following configuration is invalid and will result in a CORS error:

Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true

Browsers do not allow wildcard * origins when Access-Control-Allow-Credentials: true is set.

Fix:
Set a specific origin instead of: *

Access-Control-Allow-Origin: https://yourdomain.com
Access-Control-Allow-Credentials: true

For Express.js (Node.js):

app.use((req, res, next) => {
  res.header("Access-Control-Allow-Origin", "https://yourdomain.com"); // No '*'
  res.header("Access-Control-Allow-Credentials", "true");
  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-Credentials'] = 'true'
    return response

2. Credentials Not Sent from Client

Issue:
Even if the server allows credentials, the client must explicitly send them. If not, the browser will not include cookies, authorization headers, or TLS certificates.

Fix:
Ensure the client request includes credentials: "include" in JavaScript:

Fetch API:

fetch("https://yourdomain.com/api", {
  method: "GET",
  credentials: "include", // Important!
})
  .then((res) => res.json())
  .then((data) => console.log(data))
  .catch((err) => console.error(err));

Axios:

axios.get("https://yourdomain.com/api", { withCredentials: true })
  .then(response => console.log(response.data))
  .catch(error => console.error(error));

3. Preflight Requests Must Also Include Credentials

Issue:
If the request triggers a preflight request OPTIONS the server must also allow credentials in the preflight response.

Fix:
Ensure preflight requests OPTIONS method return the correct headers:

For Express.js:

app.options("*", (req, res) => {
  res.header("Access-Control-Allow-Origin", "https://yourdomain.com");
  res.header("Access-Control-Allow-Credentials", "true");
  res.header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
  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-Credentials'] = 'true'
    response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, OPTIONS'
    return response

4. Missing Vary: Origin Header for Dynamic Origins

Issue:
If your API serves multiple frontends dynamically (e.g., allowing multiple origins), CORS may fail without the Vary: Origin header.

Fix:
For dynamic origins, return the Vary: Origin header to prevent caching issues:

For Express.js:

app.use((req, res, next) => {
  const allowedOrigins = ["https://site1.com", "https://site2.com"];
  const origin = req.headers.origin;
  
  if (allowedOrigins.includes(origin)) {
    res.header("Access-Control-Allow-Origin", origin);
    res.header("Access-Control-Allow-Credentials", "true");
    res.header("Vary", "Origin");
  }
  next();
});

For Flask:

@app.after_request
def add_cors_headers(response):
    allowed_origins = ["https://site1.com", "https://site2.com"]
    origin = request.headers.get("Origin")

    if origin in allowed_origins:
        response.headers["Access-Control-Allow-Origin"] = origin
        response.headers["Access-Control-Allow-Credentials"] = "true"
        response.headers["Vary"] = "Origin"

    return response

Conclusion

The “Misconfigured Access-Control-Allow-Credentials” error is typically caused by:

  • Using a wildcard origin * instead of a specific one.
  • Forgetting to send credentials from the client credentials: "include"
  • Missing preflight CORS headers.
  • Not handling multiple origins correctly.