5

I used to run nginx v1.6 with this configuration :

location / {
    alias                   /some/path/;
    proxy_set_header        X-Real-IP $remote_addr;
    proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header        X-Forwarded-Proto $scheme;
    proxy_set_header        uuid $uuid;
    more_set_headers        'Access-Control-Allow-Origin: $http_origin';
    more_set_headers        'Access-Control-Allow-Credentials: true';
    more_set_headers        'Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS';
    more_set_headers        'Access-Control-Allow-Headers: DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,access_token,__setXHR_';
    if ($request_method = 'OPTIONS') {
        more_set_headers    'Access-Control-Allow-Origin: $http_origin';
        more_set_headers    'Access-Control-Allow-Credentials: true';
        more_set_headers    'Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS';
        more_set_headers    'Access-Control-Allow-Headers: DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,access_token,__setXHR_';
        add_header          'Access-Control-Max-Age' 1728000;
        add_header          'Content-Type' 'text/plain charset=UTF-8';
        add_header          'Content-Length' 0;
        return              204;
    }
}

Since my upgrade to nginx v1.10.x, "more_set_headers" isn't working anymore, and I've changed it by add_header 'blablabla' always;

It now looks like this :

location / {
    alias                   /some/path/;
    proxy_set_header        X-Real-IP $remote_addr;
    proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header        X-Forwarded-Proto $scheme;
    proxy_set_header        uuid $uuid;
    add_header              'Access-Control-Allow-Origin: $http_origin' always;
    add_header              'Access-Control-Allow-Credentials: true' always;
    add_header              'Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS' always;
    add_header              'Access-Control-Allow-Headers: DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,access_token,__setXHR_' always;
    if ($request_method = 'OPTIONS') {
        add_header          'Access-Control-Allow-Origin: $http_origin' always;
        add_header          'Access-Control-Allow-Credentials: true' always;
        add_header          'Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS' always;
        add_header          'Access-Control-Allow-Headers: DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,access_token,__setXHR_' always;
        add_header          'Access-Control-Max-Age' 1728000;
        add_header          'Content-Type' 'text/plain charset=UTF-8';
        add_header          'Content-Length' 0;
        return              204;
    }
}

However, when I now go on the website, I have this error :

Failed to load https://mywebsite/auth/login: Response to preflight request doesn't pass access control check: The 'Access-Control-Allow-Origin' header contains multiple values '$http_origin: always', but only one is allowed. Origin 'https://mywebsite' is therefore not allowed access.

What should I change to make it work ? I'm a bit stuck there.

5 Answers 5

15

I cannot remember where I read this earlier today. I lost the page. However, the answer in my case was:

you cannot set multiple cors headers in your stack. E.g.g. nginx and django. Only one of them can do the job or there will be multiple values in the header.

By removing cors handling from my api and only let nginx do it, the error disappeared.

I will update this and refer to the source when I find it. Since it is really hard to find this info on Google across multiple forums and platforms, I decided to answer this obvious thread on SO.

Update 2021 - Some more explanation

For the curious ones, to elaborate a bit more on this. A CORS header can actually only container a single value. If its a * to allow all, that's ok but what if you wanted to allow two specific IPs? You would want to write those two IPs in the header, which is not valid.

What all those packages do under the hood when using web frameworks ala Express.js or Django is the following.

They match the IP from the incoming requests referrer header against a list of IPs that was provided by the developer. If there is a match, the response will get that specific IP added in the CORS header. No other IP.

If you ever want to implement your own CORS middleware, you need to replicate the described behavior.

Nginx is very likely doing the same thing while neither side checks if there is already a value in the header. They just add the value based on match. So the result will be an invalid CORS header.

Sign up to request clarification or add additional context in comments.

1 Comment

0

But you're adding the same headers twice.
No need to add header after request method check as it's added above in any case.
So I believe your config should look like this:

location / {
    alias                   /some/path/;
    proxy_set_header        X-Real-IP $remote_addr;
    proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header        X-Forwarded-Proto $scheme;
    proxy_set_header        uuid $uuid;
    add_header              'Access-Control-Allow-Origin: $http_origin' always;
    add_header              'Access-Control-Allow-Credentials: true' always;
    add_header              'Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS' always;
    add_header              'Access-Control-Allow-Headers: DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,access_token,__setXHR_' always;
    if ($request_method = 'OPTIONS') {
        add_header          'Access-Control-Max-Age' 1728000;
        add_header          'Content-Type' 'text/plain charset=UTF-8';
        add_header          'Content-Length' 0;
        return              204;
    }
}

1 Comment

Yes, tried that, but got another error message : Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'mywebsite' is therefore not allowed access.
0

Solution found.

It was "simply" a syntax mistake.

add_header              'Access-Control-Allow-Origin: $http_origin' always;

must in fact be

add_header              'Access-Control-Allow-Origin' '$http_origin' always;

And same modification for all the others add_header entries of course.

1 Comment

You should in fact neither allow not deny it on nginx if you already do it anywhere else in you stack. That is what my answer is saying. You should not set this header at all in nginx. But in my opinion it's better to set all header on nginx and don't do it on application level.
0

Based on the explication of The Fool ( first answer).
I resolved the problem by block nginx to set headers by delete proxy_set_header Host $host; from nginx configuration

server {
   ....
   location / {
      ...
   #  proxy_set_header Host $host;  
   }}
 

Comments

0

add_header will append value instead of replacing it

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.