Configuring Cross-Origin Resource Sharing (CORS) Correctly

Cross-Origin Resource Sharing (CORS) enforces security policies that restrict which origins may access resources on a server. Proper configuration is essential to allow legitimate cross-origin interactions while preventing unauthorized access.

CORS Activation Scenarios

A request triggers CORS processing when it crosses origins and meets certain conditions:

  • Same origin: Request porceeds normally.
  • Different origin, simple request: Sent directly; server validates response headers.
  • Different origin, non-simple request: Browser first sends an OPTIONS preflight request. If the server approves, the actual request follows.

A simple request satisfies all of:

  1. Method is GET, POST, or HEAD.
  2. Only safe headers are present (Accept, Accept-Language, Content-Language, Content-Type limited to application/x-www-form-urlencoded, multipart/form-data, text/plain).

Non-simple requests invoke a preflight because browsers skip the preliminary check for simple ones. This means the server still executes the request even if the client cannot read the response due to missing or incorrect CORS headers, potentially causing security issues.

Preflight example initiated by browser:

OPTIONS /resource/data HTTP/1.1
Origin: https://untrusted-site.org
Sec-Fetch-Dest: empty
Sec-Fetch-Site: cross-site
Sec-Fetch-Mode: cors

Server response permitting the operation:

HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://untrusted-site.org
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization

Recommended Configuration Practices

Origin Whitelisting

Implement middleware that inspects the Origin header before request handling. Permit the request only if the origin matches a predefined list. For OPTIONS requests, immediately reply with 204 No Content. Otherwise, continue processing.

Allowed Methods

Specify exact methods instead of using *. Define a whitelsit such as GET, POST, PUT, DELETE.

Permitted Headers

Avoid wildcard for headers. Explicitly enumerate allowed entries. Authorization must always be listed individually. When serving range-based media, include Range in the whitelist.

Credential Handling

Enable credentials by setting Access-Control-Allow-Credentials to true. In this mode, Access-Control-Allow-Origin must specify a concrete origin—not *.

Exposed Headers

Optionally set Access-Control-Expose-Headers to make additional response headers readable by client-side scripts when they fall outside the default exposed set.

Implementation Example (Node.js/Express)

const trustedDomains = ['https://client-app.com', 'https://service.client-app.com'];

app.use((request, response, nextFn) => {
  const incomingOrigin = request.headers.origin;
  if (trustedDomains.includes(incomingOrigin)) {
    response.set('Access-Control-Allow-Origin', incomingOrigin);
    response.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
    response.set('Access-Control-Allow-Headers', 'Content-Type, Authorization');
    response.set('Access-Control-Allow-Credentials', 'true');
  }

  if (request.method === 'OPTIONS') {
    return response.status(204).end();
  }

  nextFn();
});

Additional considerations:

  • Cache preflight responses via Access-Control-Max-Age to reduce repeated checks.
  • On CORS failure, browsers log detailed errors in developer tools but block JavaScript from accessing the response body, limiting error insight programmatically.

Tags: cors Security web development HTTP Headers Cross-Origin

Posted on Sat, 17 Oct 2026 16:52:39 +0000 by Bijan