Skip to content

Instantly share code, notes, and snippets.

@michiel
Created July 5, 2011 10:41
Show Gist options
  • Save michiel/1064640 to your computer and use it in GitHub Desktop.
Save michiel/1064640 to your computer and use it in GitHub Desktop.
Wide-open CORS config for nginx
#
# Wide-open CORS config for nginx
#
location / {
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
#
# Om nom nom cookies
#
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
#
# Custom headers and headers various browsers *should* be OK with but aren't
#
add_header 'Access-Control-Allow-Headers' 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
#
# Tell client that this pre-flight info is valid for 20 days
#
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain charset=UTF-8';
add_header 'Content-Length' 0;
return 204;
}
if ($request_method = 'POST') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
}
if ($request_method = 'GET') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
}
}
@hasangilak
Copy link

syntax error. missing closing ' in checking OPTIONS. 'Content-Length should be 'Content-Length'
add_header 'Content-Length 0;

@hasantayyar
Copy link

@nikitasius
Copy link

set $cors_origin "https://your.domain";

if ($http_origin ~* ^(https?://[\w\.\-]+(:\d+)?)/?.*?$  ) {
	set $cors_origin $1;
}
	
add_header Access-Control-Allow-Origin $cors_origin always;
add_header Access-Control-Allow-Credentials true;
add_header Access-Control-Allow-Methods 'GET, POST, PUT, DELETE, OPTIONS' always;
add_header Access-Control-Allow-Headers 'Accept,Authorization,Cache-Control,Content-Type,DNT,If-Modified-Since,Keep-Alive,Origin,User-Agent,X-Requested-With' always;
  • Access-Control-Allow-Methods/Access-Control-Allow-Headers use what you prefer.
  • this way (as all ways with http_origin) required Origin header to be sent to server.
  • if no Origin or trash values - it will use default (the hardcoded value https://your.domain)

@joeyhub
Copy link

joeyhub commented May 21, 2019

set $cors_origin "https://your.domain";

If you want a wild card domain, I'd set the default to that. The problem is, the origin header isn't always set from what I'm seeing. Vary seems to fix that.

The always directive just means use it for any status code.

That can be some confusion as not everyone who wants "withCredentials" actually wants anything to do with credentials so the security concern goes poof.

There's another option not included of also falling back to the referrer. It's really hard to find out how if actually works in nginx. Nginx configuration doesn't appear to make use of an ordered map or ordered list and the most prevalent documentation on the matter is confusing.

Once you need to go into if space, you might find yourself really wanting a module for this. If the sample you posted goes into something with another if statement then it can be wiped out if another if matches.

My instinct for security also tells me that you might want to validate the origin header though I have to wonder how far to really go with this. Can the user inject anything malicious? If they try to inject a new line then they'll just be sending another header rather than injecting a response header. I suppose the only thing they might do with something reflecting what's sent is try to exploit some client header handling exploit which seems a bit far fetched (and the use of . doesn't do much to prevent that).

if ($http_origin ~* ^(https?://[\w\.\-]+(:\d+)?)/?.*?$ ) { isn't even valid nginx syntax. I don't know what regex it's supporting but it looks like it's bailing on valid perl/pcre regex for this line.

@alexisrolland
Copy link

alexisrolland commented May 26, 2019

I'm facing the following error message. Any idea how to solve it please?

Access to XMLHttpRequest at 'https://localhost:8080/sockjs-node/info?t=1558864429094' from origin 'https://localhost' has been blocked by CORS policy: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.

Here is my config:

location /sockjs-node/ {
        if ($request_method = 'OPTIONS') {
          add_header 'Access-Control-Allow-Origin' '*';
          add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
          add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
          add_header 'Access-Control-Max-Age' 1728000;
          add_header 'Content-Type' 'text/plain; charset=utf-8';
          add_header 'Content-Length' 0;
          return 204;
        }
        if ($request_method = 'POST') {
          add_header 'Access-Control-Allow-Origin' '*';
          add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
          add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
          add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
        }
        if ($request_method = 'GET') {
          add_header 'Access-Control-Allow-Origin' '*';
          add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
          add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
          add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
        }

        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_pass http://app/sockjs-node;
      }

I have also tried with the following in all 3 "if" blocks:

add_header "Access-Control-Allow-Origin" $http_origin;

I get the same error message as above.

I ahve also added the following in all 3 "if" blocks in addition to "$http_origin":

add_header 'Access-Control-Allow-Credentials' 'true';

I get the following error message:

sockjs.js?3600:1605 POST https://localhost:8080/sockjs-node/718/n0jb5hss/xhr_streaming?t=1558864860566 404

Access to XMLHttpRequest at 'https://localhost:8080/sockjs-node/718/n0jb5hss/xhr_streaming?t=1558864860566' from origin 'https://localhost' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

GET https://localhost:8080/sockjs-node/718/2rgh44pm/eventsource 404

@evtuhovdo
Copy link

On add_header, if your server will intentionally throw status code other than 200, 201, 204, 206, 301, 302, 303, 304, or 307 (Ex. 400 or 422) you should add always on each line

So instead of

add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Credentials' 'true';

Use

add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Credentials' 'true' always;

Source

thx!

@mrbrazzi
Copy link

mrbrazzi commented Jan 14, 2020

I hope my recent solution help to someone, check this https://gist.github.com/alexjs/4165271#gistcomment-3138623

@imdoingotherthings
Copy link

I'm facing the following error message. Any idea how to solve it please?

Access to XMLHttpRequest at 'https://localhost:8080/sockjs-node/info?t=1558864429094' from origin 'https://localhost' has been blocked by CORS policy: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.

Here is my config:

location /sockjs-node/ {
        if ($request_method = 'OPTIONS') {
          add_header 'Access-Control-Allow-Origin' '*';
          add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
          add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
          add_header 'Access-Control-Max-Age' 1728000;
          add_header 'Content-Type' 'text/plain; charset=utf-8';
          add_header 'Content-Length' 0;
          return 204;
        }
        if ($request_method = 'POST') {
          add_header 'Access-Control-Allow-Origin' '*';
          add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
          add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
          add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
        }
        if ($request_method = 'GET') {
          add_header 'Access-Control-Allow-Origin' '*';
          add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
          add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
          add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
        }

        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_pass http://app/sockjs-node;
      }

I have also tried with the following in all 3 "if" blocks:

add_header "Access-Control-Allow-Origin" $http_origin;

I get the same error message as above.

I ahve also added the following in all 3 "if" blocks in addition to "$http_origin":

add_header 'Access-Control-Allow-Credentials' 'true';

I get the following error message:

sockjs.js?3600:1605 POST https://localhost:8080/sockjs-node/718/n0jb5hss/xhr_streaming?t=1558864860566 404

Access to XMLHttpRequest at 'https://localhost:8080/sockjs-node/718/n0jb5hss/xhr_streaming?t=1558864860566' from origin 'https://localhost' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

GET https://localhost:8080/sockjs-node/718/2rgh44pm/eventsource 404

This is fantastic. It helped me solve my issue!

@winnerwbx
Copy link

add_header Access-Control-Allow-Origin $http_origin;
add_header Access-Control-Allow-Credentials true;

would work

@alex-r89
Copy link

Hi,

Is anyone able to assist with my issue. I am seeing that my cookie is coming through in the response headers, but not being saved on the browser. I am only noticing this in production (where I am using Nginx and SSL) and not in development (which is not using a proxy and not using SSL). I am using Express (Node) and Nginx on Ubuntu 18.04

My configurations are as follows:

Nginx (/etc/nginx/sites-available/default)

    server_name api.myBackendURL.net;

    location / {
    proxy_pass http://localhost:4000;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection 'upgrade';
    proxy_set_header Host $host;
    proxy_cache_bypass $http_upgrade;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_http_version 1.1;
   }

Cookie values, using express-session and cors

  const sessionOption: session.SessionOptions = {
    store: new RedisStore({
      client: redis
    }),
    name: 'qid',
    secret: 'njw32323ndiodqwnlad3',
    resave: false,
    proxy: process.env.NODE_ENV === 'production',
    saveUninitialized: false,
    cookie: {
      // Values from next examples with-passport
      domain: .myFrontendURL.app,
      maxAge: 1000 * 60 * 60 * 24 * 91, // 3 month cookie
      expires: new Date(Date.now() + 1000 * 60 * 60 * 24 * 365 * 1000),
      httpOnly: true,
      secure: process.env.NODE_ENV === 'production',
      path: '/',
      sameSite: 'lax'
    }
  }
  app.use(
    cors({
      origin: "https://subdomain.myFrontendURL.app",
      credentials: true
    })
  )

Headers in the Response

Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: https://subdomain.myFrontendURL.app,
Connection: keep-alive
Content-Length: 151
Content-Type: application/json; charset=utf-8
Date: Sat, 13 Jun 2020 10:18:28 GMT
ETag: W/"97-gYNuyhWohicUF549wSDDpEE4o3M"
Server: nginx/1.14.0 (Ubuntu)
Set-Cookie: qid=s%3AtP2fO9aX1Lo7uU_dLKOewqC9zxf8B-2P.vXSlE1khTK6Qe6O0lIn7g1WxJ1NIJyFHFoff8Jl1IO0; Path=/; HTTPOnly; Secure; Expires=Fri, 15 Oct 3019 09:47:00 GMT; HttpOnly; Secure; SameSite=Lax
Vary: Origin
X-Powered-By: Express

If it's of any use, the request headers are as follows:

accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-GB,en-US;q=0.9,en;q=0.8
Connection: keep-alive
Content-Length: 282
content-type: application/json
Host: api.myBackendURL.net
Origin: https://subdomain.myFrontendURL.app
Referer: https://subdomain.myFrontendURL.app/
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.97 Safari/537.36

Just to note, sudo nginx -t returns test is successful, and I have restarted nginx etc.

@DictatorL
Copy link

So, i've never done anything with Nginx, cors, and have absolutely zero coding knowledge or experience, in a layman's terms, how in the world am i supposed to use any of this? It doesn't seem to work no matter how i do it. I'm using nginx 1.19.2

@Meekohi
Copy link

Meekohi commented Aug 23, 2020

@DictatorL -- you'll find a file nginx/conf/nginx.conf in your installation that describes how nginx should accept traffic and route it to your application (here are some pre-canned ones you can use https://www.nginx.com/resources/wiki/start/#pre-canned-configurations). This gist shows you a few different examples of how you might setup CORS by using the add_header options, the exact one for you will depend on what you're trying to accomplish, you can learn more at https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS... although as I pointed out above using a wildcard for the origin is not correct, use this instead:

add_header "Access-Control-Allow-Origin" $http_origin;

@eazy-rydah
Copy link

@DictatorL -- you'll find a file nginx/conf/nginx.conf in your installation that describes how nginx should accept traffic and route it to your application (here are some pre-canned ones you can use https://www.nginx.com/resources/wiki/start/#pre-canned-configurations). This gist shows you a few different examples of how you might setup CORS by using the add_header options, the exact one for you will depend on what you're trying to accomplish, you can learn more at https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS... although as I pointed out above using a wildcard for the origin is not correct, use this instead:

add_header "Access-Control-Allow-Origin" $http_origin;

That worked!

@huynq0410
Copy link

Saved my days. I use:

add_header "Access-Control-Allow-Origin" $http_origin;
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT';

@marcosrjalves
Copy link

For firefox, i installed this plugin
https://add0n.com/access-control.html?version=0.1.9&type=install

After the nginx config above, I was still getting cors erros.
Just installed this plugin and it works

@anphetamina
Copy link

not working for me, I always get 304 redirects on backend requests

@raselsys
Copy link

@GromNaN thanks a lot :D
If you use a cache server, you must add a Vary: Origin to the response.

add_header "Access-Control-Allow-Origin" $http_origin;
add_header "Vary" "Origin";

It worked for allhost.my-domain.com

@PauloJFCabral
Copy link

I try this configuration, but it is not working. I have a backend server with my code (laravel app) Do I need to have some configurations?

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