Skip to content

Turn on the CORS by Feng Shui... maybe ๐Ÿ™ƒ

Notifications You must be signed in to change notification settings

RubaXa/nginx-cors

Folders and files

NameName
Last commit message
Last commit date

Latest commit

a2bd6d5 ยท Nov 25, 2020

History

38 Commits
Jan 5, 2020
Nov 25, 2020

Repository files navigation

nginx-cors

Turn on the CORS by Feng Shui... maybe ๐Ÿ™ƒ

Featuring

  • Easy and clear to use
  • Does not use add_header into if (therefore, your headers will not be lost)
  • Configurable and flexibility

Articles


Install

cd /usr/local/etc/nginx/
git clone git@github.com:RubaXa/nginx-cors.git

Hot to use

cd /usr/local/etc/nginx/
vim ./cors-enabled.conf

1. Create ./cors.setup.conf

CORS confirugration

# If in your project setted larger, then this directive is not required
map_hash_bucket_size 256;

# Setup the $cors_path variable
map $request_uri $cors_path {
  ~^(?<path>[^?]+) $path;
}

# Convert Endpoints to CORS service
map "$scheme://$host$cors_path" $cors_service {
  "https://api.project.com/user/info" "cors.service.user-info";
  ~^https:\/\/api.auth.project.com\/(login|logout)$ "cors.service.auth";
  default "<<unknown>>";
}

# Convert Origin to CORS client
map "$http_origin" $cors_client {
  # "cors.client.foo" or "cors.client.bar";
  ~^https:\/\/(foo|bar)\.client\.com$ "cors.client.$1";
  default "<<unknown>>";
}

# Turn on CORS by client and service map
map "$cors_client -> $cors_service" $cors_enabled {
  # Access for 'foo' client
  "cors.client.foo -> cors.service.auth"      "true";
  "cors.client.foo -> cors.service.user-info" "true";

  # Access for 'bar' client
  "cors.client.bar -> cors.service.auth" "true";
}

2. Enable CORS on any endpoint

http {
  # 1๏ธโƒฃSetup CORS for all services (!)
  include 'cors.setup.conf';

  server {
    listen 80;
    server_name api.project.com;

    location /user/info {
      # 2๏ธโƒฃEnable CORS without credentials/cookies (!!)
      include 'nginx-cors/cors.conf';
    }
  }

  server {
    listen 80;
    server_name api.auth.project.com;

    location ~/(login|logout) {
      # 3๏ธโƒฃ Enable CORS with credentials/cookies (!!!)
      set $cors_allow_credentials "true";
      include 'nginx-cors/cors.conf';
    }
  }
}

OR without cors service/client (not recommended, see compare)

location /api/user/exists {
  if ($http_origin = 'https://client.com') {
    set $cors_enabled 'true';
  }
  include 'nginx-cors/cors.conf';
}

Variables

See detail in source: cors.conf


Please send a PR if you know how to make it more beautiful and correctly.

Thx.


Compare

Classical bugy way ๐Ÿ’ฉ

  • It's hard to read who exactly enabled CORS
  • It is easy to make a mistake in RegExp
  • If there is an add_header inside, it will not work
location ~ '/api/auth/(login|logout)' {
  if ($http_origin ~ '^https?://(about|company|account)\.(((alpha|beta|omega)\.))?test\.)?)?business\.com$')
    set $cors 'enabled';
  }

  add_header X-My-Header "true"; # it will not work ๐Ÿž

  if ($request_method = 'OPTIONS') {
    set $cors "preflight-${cors}";
  }

  if ($cors = 'preflight-enabled') {
    add_header Access-Control-Allow-Origin $http_origin;
    add_header Access-Control-Allow-Headers "...";
    add_header Access-Control-Allow-Methods "...";
    add_header Access-Control-Expose-Headers "X-My-Header";
    return 204;
  }

  if ($cors = 'enabled') {
    add_header X-My-Header "true"; # but here it will work (1)
    add_header Access-Control-Allow-Origin $http_origin;
  }

  # bla-bla-bla
}

True way ๐Ÿ‘

  • Easy to read and maintain.
  • Flexible configuration
  • Operating not at the RegExp level, but client -> service
# Setup the $cors_path variable
map $request_uri $cors_path {
  ~^(?<path>[^?]+) $path;
}

# Convert endpoints to CORS service
map "$scheme://$host$cors_path" $cors_service {
  ~^https:\/\/project.com/api/(login|logout)$ "cors.service.auth";
  default "<<unknown>>";
}

# Convert Origin to CORS client
map "$http_origin" $cors_client {
  # Yes, it would be possible to write in one regexp, but it is much more readable and more flexible.
  # In addition, you need to list subdomains! Why so? See next `map` ;]
  ~^https:\/\/([^\.]+)\.business\.com$" "cors.client.business.$1";
  ~^https:\/\/([^\.]+)\.test\.business\.com$" "cors.client.business.$1";
  ~^https:\/\/([^\.]+)\.(alpha|beta|omega)\.test\.business\.com$" "cors.client.business.$1";
  default "<<unknown>>";
}

# Turn on CORS by client and service map
map "$cors_client -> $cors_service" $cors_enabled {
  # And now we can clearly and elegantly include rights
  # based on the "$cors_client" and the "$cors_service" to which "client" invoke!
  "cors.client.business.about -> cors.service.auth"   "false";
  "cors.client.business.company -> cors.service.auth" "true";
  "cors.client.business.account -> cors.service.auth" "true";
  default "false";
}

and

location ~ '/api/auth/(login|logout)' {
  add_header X-My-Header "true"; # It's work! ๐ŸŽ‰
  set $cors_allow_expose_headers "X-My-Header";
  include "ngxin-cors/cors.conf";
}