Skip to content

Instantly share code, notes, and snippets.

@jprjr
Last active June 11, 2020 16:15
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jprjr/1a723b18335bcc455ac5 to your computer and use it in GitHub Desktop.
Save jprjr/1a723b18335bcc455ac5 to your computer and use it in GitHub Desktop.
Server-level CORS in NGINX
if ($http_origin ~* https?://(?:.*\.)?example\.(?:org|net|com)(?::[0-9]+)? ) {
set $cors 'true';
}
# test for empty variables will default to 'testtrue'
set $cors_allow_origin_test "test${cors}${cors_allow_origin}";
set $cors_allow_credentials_test "test${cors}${cors_allow_credentials}";
set $cors_allow_headers_test "test${cors}${cors_allow_headers}";
set $cors_allow_methods_test "test${cors}${cors_allow_methods}";
set $cors_max_age_test "test${cors}${cors_max_age}";
if ($cors_allow_origin_test = 'testtrue') {
set $cors_allow_origin "$http_origin";
}
if ($cors_allow_credentials_test = 'testtrue' ) {
set $cors_allow_credentials 'true';
}
if ($cors_allow_headers_test = 'testtrue') {
set $cors_allow_headers 'Authorization,Content-Type,Accept,Origin,User-Agent,DNT,Cache-Control,X-Mx-ReqToken,Keep-Alive,X-Requested-With,If-Modified-Since';
}
if ($cors_allow_methods_test = 'testtrue') {
set $cors_allow_methods 'GET, POST, OPTIONS';
}
if ($cors_max_age_test = 'testtrue') {
set $cors_max_age 172800;
}
if ($request_method = 'GET') {
set $cors_method "${cors}get";
# Blank out headers we don't need
set $cors_allow_headers '';
set $cors_allow_methods '';
set $cors_max_age '';
}
if ($request_method = 'POST') {
set $cors_method "${cors}post";
# Blank out headers we don't need
set $cors_allow_headers '';
set $cors_allow_methods '';
set $cors_max_age '';
}
if ($request_method = 'OPTIONS') {
set $cors_method "${cors}options";
}
# Headers valid for GET/POST/OPTIONS
add_header 'Access-Control-Allow-Origin' $cors_allow_origin;
add_header 'Access-Control-Allow-Credentials' $cors_allow_credentials;
# Headers valid for OPTIONS
add_header 'Access-Control-Max-Age' $cors_max_age;
add_header 'Access-Control-Allow-Methods' $cors_allow_methods;
add_header 'Access-Control-Allow-Headers' $cors_allow_headers;
if ($cors_method = 'trueoptions') {
return 204;
}
user www-data;
worker_processes 1;
daemon off;
events {
worker_connections 1024;
}
http {
server_tokens off;
include /etc/nginx/mime.types;
default_type application/octet-stream;
keepalive_timeout 65;
server {
listen 80 default;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
root /srv/www;
index index.html;
include /etc/nginx/includes/cors.conf;
location /app/ {
proxy_pass http://backend/app/;
}
}
}

NGINX does weird things with If statements: http://wiki.nginx.org/IfIsEvil

It turns out, "if" inside a location directive w/ proxy_pass disables that implied URL-rewriting function. So, for example:

location /some/url/ {
  proxy_pass http://backend/api/;
  # example client url: http://server/some/url/data
  # URL sent to proxy: http://backend/api/data
}

location /another/url/ {
  set $test 1;
  if ($test) {
    # do nothing
  }
  proxy_pass http://backend/api/;
  # example client url: http://server/another/url/data
  # URL sent to proxy: http://backend/api/another/url/data
}

So I opted to move all my CORS-handling stuff to the server block, since I want CORS enabled across the board anyways, it makes sense.

It also turns out, you can't use "add_header" inside an "if" statement at the server block. But, you can set variables, and if you have something like add_header 'X-Header-Test' $empty_var; - the header doesn't get added. Your variable needs data.

So, this NGINX config is meant to be used as an included file. Before including, you can set variables to customize it:

  • $cors_allow_origin - set this to '*' or 'null', leave blank to send back the origin 'Origin' header
  • $cors_allow_credentials - set this to true or false to allow/disallow sending credentials. defaults to true.
  • $cors_max_age - set this to the number of seconds to cache pre-flight results, defaults to 48 hours
  • $cors_allow_methods - set this to a list of allowed methods, if blank you'll get a default set of methods.
  • $cors_allow_headers - set this to a list of allowed headers, if blank you'll get a default set of common headers.
@jgonera
Copy link

jgonera commented Dec 12, 2018

Thanks for this! You probably want $cors_max_age set to 172800 though (one more zero).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment