Why Does a POST Trigger Two Requests? A Deep Dive into CORS Preflight
The article explains why a POST request can result in two HTTP calls by detailing the same‑origin policy, the conditions that make a request simple, and how browsers automatically issue a CORS preflight OPTIONS request before the actual POST.
Introduction
During a ByteDance interview the author was asked why a POST request sometimes generates two HTTP requests. The answer lies in the browser's security mechanisms, especially the Same‑Origin Policy and CORS.
Same‑Origin Policy
The Same‑Origin Policy restricts how a document or script from one origin can interact with resources from another origin. Two URLs are considered same‑origin only when their protocol, host, and port are identical.
It protects against attacks such as XSS, SQL injection, OS command injection, HTTP header injection, CSRF, and others. The policy manifests in three areas:
DOM access restriction – scripts cannot read or manipulate the DOM of a cross‑origin page.
Web data restriction – XMLHttpRequest or fetch can only request resources from the same origin unless CORS headers are present.
Network communication restriction – browsers block cross‑origin network responses that lack proper CORS approval.
CORS (Cross‑Origin Resource Sharing)
CORS provides a controlled way for browsers to request resources from a different origin. If a request does not meet the criteria for a simple request, the browser first sends a preflight OPTIONS request.
Simple Request Conditions
HTTP method must be GET, HEAD or POST.
Allowed request headers are limited to Accept, Accept-Language, Content-Language, Last-Event-ID, and Content-Type (only application/x-www-form-urlencoded, multipart/form-data, text/plain).
No ReadableStream objects are used in the request body.
No custom request headers are present.
The XMLHttpRequestUpload object has no registered event listeners.
Preflight Request
When a request is not simple, the browser automatically sends an OPTIONS request to the server to determine whether the actual request is allowed. This preflight request includes special headers: Access-Control-Request-Method: the HTTP method the actual request will use (e.g., POST). Access-Control-Request-Headers: a comma‑separated list of custom headers the actual request will send (e.g., content-type,x-secsdk-csrf-token). Access-Control-Allow-Origin: the origin that is permitted (e.g., https://xxx.cn or *). Access-Control-Max-Age (optional): how long the preflight response can be cached, expressed in seconds (e.g., 86408 seconds ≈ 1 day).
After the server approves the preflight request, subsequent actual requests include an Origin header, and the server must respond with Access-Control-Allow-Origin for the browser to expose the response.
Example: Deleting a record on a different domain first triggers a preflight OPTIONS request, then the real POST request if the server returns the appropriate CORS headers.
Credentials and Wildcards
When a request carries credentials (e.g., cookies), the server must not use a wildcard * for Access-Control-Allow-Origin. It should specify the exact origin, such as: Access-Control-Allow-Origin: https://xxx.cn Similarly, Access-Control-Allow-Headers and Access-Control-Allow-Methods should list explicit values rather than * to avoid security risks.
Full Request Flow
The following diagram illustrates the complete CORS request flow, including the preflight step and the final response.
Conclusion
A preflight request is an automatic OPTIONS request issued by the browser during a cross‑origin POST (or any non‑simple request). It ensures that the server explicitly permits the cross‑origin operation, thereby protecting user data and privacy.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Java Architect Handbook
Focused on Java interview questions and practical article sharing, covering algorithms, databases, Spring Boot, microservices, high concurrency, JVM, Docker containers, and ELK-related knowledge. Looking forward to progressing together with you.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
