Cross-origin HTTP requests

What is it?

  • Browser mechanism for preventing requests of third party  resources for an origin
  • Enables a more controlled environment for resources by restricting origins with an access list
  • Gives us a way of disabling certain features on our resources depended on their origin

A dictionary

  • Request - an action a browser makes to get or send data to an endpoint, it can contain a payload
  • Simple request - a request not triggering preflight check
  • Header - "arbitrary" key value pairs sent with every HTTP request

When is it triggered?

  • When you are requesting a resource that is not on the same domain as the origin (eg. localhost and staging API)
  • When you are requesting a resource from an unlisted endpoint (eg. anywhere else except on

Preflighted requests

When are you going to get flagged?

  • If you are not using GET, POST, or HEAD 
  • If you are using any custom headers apart from Content* and a like (full list on MDN) 
  • If you are not using standard Content-types (application/x-www-form-urlencoded, multipart/form-data, text/plain)

What's gonna happen to me?

  • An OPTIONS request is sent out to determine the security of our request
  • If everything goes right (you have correct headers set on the response) a second request is going to be made
  • If the request fails an exception occurs 

Configuring CORS by the book

Setting the correct headers

  • Access-Control-Allow-Origin - comma separated list of domains (wildcards allowed)
    • Access-Control-Allow-Origin ''
  • Access-Control-Allow-Methods - comma separated list of methods
    • Access-Control-Allow-Methods 'GET, OPTIONS'
  • Access-Control-Allow-Headers - any custom request headers needed
    • Access-Control-Allow-Headers 'X-Bearer'
  • Access-Control-Expose-Headers - any custom response headers needed

Where do I set it?

  • On your web server (Apache, Nginx)
  • On app level (express, koa)
  • Both


Developing with a
ill-configured CORS

Easiest solution

fetch('', {
    method: 'GET',
    mode: 'no-cors'
}).then((e) => {
    // type: 'opaque'
    // body: 'null'


  • Kill all instances of Chrome running (same version of, you can have this on Canary only e.g.)
  • Run that same version from terminal with flag

Remote Proxy

  • Prefix all requests with
  • Dirty hack
  • Maybe violating NDA (uncontrolled resource where the data is passing thru)

Webpack dev server

proxy: {
  "/api": {
    target: "",
    secure: false, // can even turn HTTPS certificate validation off
    pathRewrite: {"^/api" : ""}

Local Proxy

const httpProxy = require('http-proxy');

const proxy = httpProxy
    target: '',
    secure: false
    (_, req, res) => {
      res.setHeader('Access-Control-Allow-Origin', '*');
      res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
      res.setHeader('Access-Control-Allow-Headers', 'HTTP_USER_AGENT');
      res.setHeader('Access-Control-Expose-Headers', 'X-API-authentication-token');
Method Secure No coding No Sep. Srv. Scalable
Changing mode yes no * yes no
Browser no yes yes yes
Remote proxy no yes yes yes
Webpack dev server yes no yes yes
Local proxy yes no no yes


  • You need and want CORS on all resources
  • Configure CORS with development in mind only on staging servers
  • Follow the standard and set all of the needed headers to reduce issues and debugging
  • Use a local proxy whenever possible for development
  • Easiest way to configure CORS for production is not to touch anything (use reverse proxy on Apache/Nginx)
  • Don't disable security in your browser
  • Stop guessing what's needed for a request to go thru and read up
  • Remember it's a browser thing - it won't stop randos

References and resources

  • (mind the NDA tho)


By Andrei Zvonimir Crnković


Everything you wanted to know about CORS but were to lazy to Google

  • 274
Loading comments...

More from Andrei Zvonimir Crnković