Securing API Endpoints with CORS Headers

Cross-Origin Resource Sharing (CORS) is a vital browser-enforced security standard that governs how web applications interact with APIs hosted on different domains. In distributed web development, where frontends and microservices frequently operate across disparate origins, CORS headers dictate which external domains are permitted to read sensitive API responses. This article explains the mechanics of CORS, highlights how specific response headers defend backend services against unauthorized cross-origin access, and outlines essential configuration practices for distributed architectures.

Understanding the Same-Origin Policy and CORS

Web browsers enforce the Same-Origin Policy (SOP) by default. Under SOP, a web application can only request resources from the same origin—defined by an identical scheme (protocol), host (domain), and port—as the page serving the application.

When modern architectures separate the frontend (e.g., https://app.example.com) from backend APIs (e.g., https://api.example.com), the browser treats API calls as cross-origin requests and blocks the frontend from reading the responses unless the server explicitly permits it. CORS acts as the controlled relaxation of the Same-Origin Policy, allowing servers to instruct browsers on which external origins are trusted.

How CORS Headers Protect API Endpoints

CORS is strictly a client-side security control enforced by browsers, not the server itself. While it does not prevent direct server-to-server or tool-based (such as cURL) requests, it protects APIs by preventing malicious websites from abusing a user's authenticated browser session.

1. Preventing Unauthorized Data Leakage

When an authenticated user visits an untrusted website, that malicious site could execute JavaScript to query your API. If the user has active session cookies, the browser automatically attaches them. Without CORS headers, the browser would allow the malicious script to read sensitive API responses. By configuring Access-Control-Allow-Origin, the API explicitly declares which domains can view the response payload. If the origin does not match, the browser drops the response immediately.

2. Preflight Checks for Destructive Actions

For requests that could alter data (e.g., PUT, DELETE, or POST with complex Content-Types), browsers send an automatic OPTIONS request called a preflight check before the actual request executes.

The preflight request asks the API for permission. If the API does not respond with valid CORS headers approving the origin, HTTP method, and headers, the browser cancels the transaction entirely. This stops unauthorized web applications from triggering state-changing operations on your backend.

Key CORS Headers in Distributed Architectures

To secure an API, backends must return specific HTTP response headers:

  • Access-Control-Allow-Origin: Specifies the allowed origin (e.g., https://frontend.example.com). Wildcards (*) should be avoided for private or authenticated APIs.
  • Access-Control-Allow-Methods: Restricts which HTTP methods (e.g., GET, POST, PUT, DELETE) the cross-origin client can execute.
  • Access-Control-Allow-Headers: Specifies which custom request headers (such as Authorization or X-API-Key) can be sent during the actual request.
  • Access-Control-Allow-Credentials: Set to true when requests need to expose cookies, HTTP authentication, or client-side SSL certificates. Browsers will automatically reject responses where Allow-Credentials is true if Allow-Origin is set to the wildcard *.
  • Access-Control-Max-Age: Defines the duration (in seconds) the browser can cache the preflight result, reducing redundant OPTIONS requests across distributed microservices.

Best Practices for API Security

  • Maintain Explicit Allowlists: Never dynamically mirror the incoming Origin header into Access-Control-Allow-Origin without validating it against an approved list of client domains.
  • Handle CORS at the Gateway: In distributed microservice environments, enforce CORS policies at the API Gateway or reverse proxy level rather than within individual services to ensure consistent policy enforcement.
  • Pair CORS with Robust Authentication: CORS does not replace token validation, OAuth, or CSRF protection; it operates alongside them to ensure that authenticated browser sessions cannot be weaponized by third-party origins.