
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 returnAccess-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.