Keywords: CORS | Cross-Origin Requests | Access-Control-Allow-Headers | Preflight Requests | Cookie Credentials
Abstract: This article provides an in-depth exploration of the Cross-Origin Resource Sharing (CORS) mechanism, analyzing the complete workflow of OPTIONS preflight requests and POST main requests through a practical case study. It focuses on the configuration requirements of key response headers such as Access-Control-Allow-Headers and Access-Control-Allow-Credentials, explains why these headers must be included in both preflight and main request responses, and offers comprehensive solutions. The article also discusses special cases of cookie transmission between subdomains, providing developers with a complete guide to cross-origin request configuration.
Overview of CORS Cross-Origin Request Mechanism
Cross-Origin Resource Sharing (CORS) is the standard mechanism for handling cross-origin requests in modern web applications. When a browser detects a cross-origin request, it decides whether to send a preflight request based on the request type and configuration. The preflight request uses the OPTIONS method to verify whether the server permits the actual cross-origin request.
Complete Workflow of Preflight and Main Requests
In the user's case, when making a POST request from domain.example to a.domain.example, the browser first sends an OPTIONS preflight request. This request includes the following key headers:
Access-Control-Request-Headers: origin, content-type, accept
Access-Control-Request-Method: POST
Origin: http://domain.example:3000The server responds to the preflight request with:
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: POST
Access-Control-Allow-Origin: http://domain.example:3000However, the server response lacks the Access-Control-Allow-Headers: origin, content-type, accept header, which causes the subsequent POST request to be blocked by the browser.
Configuration Requirements for Key Response Headers
According to the CORS specification, when cross-origin requests need to carry credentials (such as cookies), the following conditions must be met:
Access-Control-Allow-Origincannot be set to the wildcard*; it must explicitly specify the allowed origin addressAccess-Control-Allow-Credentialsmust be set totrue- All custom request headers must be explicitly listed in
Access-Control-Allow-Headers
In the user's case, the POST request uses the multipart/form-data content type, which is part of the custom request headers. The server must include Access-Control-Allow-Headers: origin, content-type, accept in the OPTIONS response to explicitly allow these headers.
Consistency Requirements Between Preflight and Main Requests
A common misconception is that CORS headers only need to be set in the OPTIONS response. In fact, according to the W3C CORS specification, the main request (such as POST) response must also include the same CORS headers. Specifically:
Access-Control-Allow-Originmust appear in both OPTIONS and POST responsesAccess-Control-Allow-Credentials: truemust also be set in both responses- If the request includes custom headers,
Access-Control-Allow-Headersshould also remain consistent across both responses
This consistency requirement ensures that the browser can correctly verify the security of the entire cross-origin request process.
Special Cases of Cookie Credential Transmission
The user mentioned that the cookie domain is set to .domain.example, which should theoretically be accessible by the subdomain a.domain.example. However, when using the withCredentials: true configuration, additional considerations include:
- The server response must include
Access-Control-Allow-Credentials: true Access-Control-Allow-Origincannot use the wildcard*- The Cookie's SameSite attribute may affect cross-origin transmission
In the user's case, since the POST request was blocked, cookies naturally could not be sent. After resolving the CORS header configuration issue, the cookie transmission problem is typically resolved as well.
Complete Solution Implementation
Based on the above analysis, the server-side should be configured as follows:
// OPTIONS request handling
app.options('/some/route', (req, res) => {
res.header('Access-Control-Allow-Origin', 'http://domain.example:3000');
res.header('Access-Control-Allow-Methods', 'POST');
res.header('Access-Control-Allow-Headers', 'origin, content-type, accept');
res.header('Access-Control-Allow-Credentials', 'true');
res.status(200).end();
});
// POST request handling
app.post('/some/route', (req, res) => {
res.header('Access-Control-Allow-Origin', 'http://domain.example:3000');
res.header('Access-Control-Allow-Credentials', 'true');
// Business logic processing
res.json({ success: true });
});The client-side JavaScript code can remain as is, since withCredentials: true is already correctly configured.
Common Issues and Debugging Recommendations
When debugging CORS issues, it is recommended to:
- Use browser developer tools to inspect network requests and confirm the complete workflow of OPTIONS and main requests
- Verify that all CORS response headers are correctly set in both preflight and main requests
- Check console error messages, as browsers usually provide detailed CORS error explanations
- Ensure that server-side middleware is correctly configured and does not omit any necessary CORS headers
By systematically checking these aspects, most CORS issues can be effectively resolved.