12
头图

When you encounter cross-domain problems, don't choose to copy and try right away. Please read this article in detail and then deal with it, I believe it can help you.

Prepare

First, ensure that the server does not handle cross-domain processing. Second, it is normal to use postman to test the server interface first.

图片

When the website 8080 accesses the server interface, a cross-domain problem occurs, so how to solve it? Next, I will list the various situations encountered across domains and solve them through nginx proxy (the background is the same, as long as you understand the principle).

Cross-domain mainly involves 4 response headers

  • Access-Control-Allow-Origin is used to set the source address that allows cross-domain requests (preflight requests and formal requests will be verified when cross-domain)
  • Access-Control-Allow-Headers Special header fields that are allowed to be carried across domains (valid only in preflight requests)
  • Access-Control-Allow-Methods Cross-domain allowed request methods or HTTP verbs (only in preflight request verification)
  • Whether Access-Control-Allow-Credentials allows the use of cookies across domains, if you want to use cookies across domains, you can add this request response header, the value is set to true (set or not set, it will not affect the request sending, only affect the Whether to carry cookies when cross-domain, but if set, both preflight requests and formal requests need to be set). However, it is not recommended for cross-domain use (used in the project, but it is unstable, and some browsers cannot carry it), unless necessary, because there are many alternatives.

Many articles on the Internet tell you that adding these response headers directly to Nginx can solve cross-domain problems. Of course, most cases can be solved, but I believe there are still many cases. Obviously, the configuration will also report cross-domain problems. .

What is a preflight request?

When a cross-domain condition occurs, the browser first asks the server whether the domain name of the current web page is in the server's allow list, and which HTTP verbs and header fields can be used. The browser will issue a formal XMLHttpRequest request only if it gets a positive answer, otherwise it will report an error. As shown below

图片

simulation

Nginx proxy port: 22222, the configuration is as follows

 server {
        listen       22222;
        server_name  localhost;
        location  / {
            proxy_pass  http://localhost:59200;
        }
}

Test whether the proxy is successful, and access the interface again through the Nginx proxy port 2222. You can see that the interface can also be accessed normally after passing through the proxy as shown in the figure below.

图片

Next, start to use the website 8080 to access the interface address after the Nginx proxy, the error is as follows ↓↓↓

Case 1
 Access to XMLHttpRequest at 'http://localhost:22222/api/Login/TestGet' from origin 'http://localhost:8080' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

图片

The error can be clearly located through the error message (note the red part). The priflight indicates that it is a pre-request. The CORS mechanism will first perform a preflight (an OPTIONS request) across domains, and the real request will only be sent after the request is successful. This is designed to ensure that the server is aware of the CORS standard to protect legacy servers that do not support CORS

Through the error message, we can get that the request response header of the preflight request is missing Access-Control-Allow-Origin. Where it is wrong, we can change it. Modify the Nginx configuration information as follows (the red part is the added part), make up what is missing, it is very simple and clear

 server {
        listen       22222;
        server_name  localhost;
        location  / {
           add_header Access-Control-Allow-Origin 'http://localhost:8080';
           proxy_pass  http://localhost:59200; 
        }
    }

Haha, when I thought it could be solved with joy, I found that I still reported the same problem

图片

But there is nothing wrong with our configuration, the problem is in Nginx, the link below is http://nginx.org/en/docs/http/ngx_http_headers_module.html

图片

add_header instruction is used to add a return header field if and only if the status codes are those listed in the figure. If you want to carry the header field information every time the response information, you need to add always at the end (after my test, only the header information of Access-Control-Allow-Origin needs to be added always, and the others will be carried back without always), then Let's try

 server {
        listen       22222;
        server_name  localhost;
        location  / {
           add_header Access-Control-Allow-Origin 'http://localhost:8080' always;
           proxy_pass  http://localhost:59200; 
        }
    }

After modifying the configuration, it is found that it takes effect. Of course, it is not solved by cross-domain. The above problem has been solved, because the content of the error message has changed.

Case 2
 Access to XMLHttpRequest at 'http://localhost:22222/api/Login/TestGet' from origin 'http://localhost:8080' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.

图片

It can be known from the error message prompt that the pre-request (option request) that is the default behavior of the cross-domain browser does not receive the ok status code. At this time, the configuration file is modified. When the request is an option request, a status code is returned to the browser. (usually 204)

 server {
        listen       22222;
        server_name  localhost;
        location  / {
           add_header Access-Control-Allow-Origin 'http://localhost:8080' always;
           if ($request_method = 'OPTIONS') {
                return 204;
           }
           proxy_pass  http://localhost:59200; 
        }
    }

When the configuration is completed, it is found that the error message has changed

Case 3
 Access to XMLHttpRequest at 'http://localhost:22222/api/Login/TestGet' from origin 'http://localhost:8080' has been blocked by CORS policy: Request header field authorization is not allowed by Access-Control-Allow-Headers in preflight response.

图片

It means that the header information authorization is missing in the pre-request response header Access-Control-Allow-Headers (various situations will be different, after cross-domain occurs, the custom-added header information is not allowed, it needs to be added to the request response header Access-Control-Allow-Headers, so that the browser knows that the carrying of this header information is recognized as legal by the server. What I carry here is authorization. , then modify the configuration file, add the corresponding missing parts, and try again

 server {
        listen       22222;
        server_name  localhost;
        location  / {
           add_header Access-Control-Allow-Origin 'http://localhost:8080' always;
           if ($request_method = 'OPTIONS') {
               add_header Access-Control-Allow-Headers 'authorization'; #为什么写在if里面而不是接着Access-Control-Allow-Origin往下写?因为这里只有预检请求才会检查
               return 204; 
          } 
        proxy_pass http://localhost:59200; 
    }
}

At this point, it was found that the error reporting problem returned to situation 1

图片

It has been tested and verified that as long as if ($request_method = 'OPTIONS') is written with add_header , the external configuration will be invalid when it is a pre-check request, why? ↓↓.

The official documentation says this:

There could be several add_header directives. These directives are inherited from the previous level if and only if there are no add_header directives defined on the current level.

This means that when there is no add_header instruction at the current level, the add_header of the previous level is inherited. On the contrary, if the current level has add_header, it should not be able to inherit the add_header of the previous level.

图片

The configuration is modified as follows:

 server {
        listen       22222;
        server_name  localhost;
        location  / {
            add_header Access-Control-Allow-Origin 'http://localhost:8080' always;
            if ($request_method = 'OPTIONS') {
                add_header Access-Control-Allow-Origin 'http://localhost:8080';
                add_header Access-Control-Allow-Headers 'authorization';
                return 204;
            }
            proxy_pass  http://localhost:59200; 
        }
    }

At this point, after the change, it is found that the cross-domain problem has been solved.

图片

However, although the above solves the cross-domain problem, it is considered that the Nginx version may be updated later. I don’t know if this rule will be modified. Considering that this way of writing may carry two Access-Control-Allow-Origin, this is also the case. Not allowed, as will be described below. So the configuration is appropriately modified as follows:

 server {
        listen       22222;
        server_name  localhost;
        location  / {
            if ($request_method = 'OPTIONS') {
                add_header Access-Control-Allow-Origin 'http://localhost:8080';
                add_header Access-Control-Allow-Headers 'authorization';
                return 204;
            }
            if ($request_method != 'OPTIONS') {
                add_header Access-Control-Allow-Origin 'http://localhost:8080' always;
            }
            proxy_pass  http://localhost:59200; 
        }
    }

It's not over yet, continue to chat ↓↓

Case 4:

Earlier APIs may only use POST and GET requests, and the request response header Access-Control-Allow-Methods only supports POST and GET by default across domains. When other request types appear, cross-domain exceptions will also occur.

For example, I changed the API interface request method of the request from the original GET to PUT, and launched a try. On the console an error is thrown:

 Access to XMLHttpRequest at 'http://localhost:22222/api/Login/TestGet' from origin 'http://localhost:8080' has been blocked by CORS policy: Method PUT is not allowed by Access-Control-Allow-Methods in preflight response.

图片

The content of the error report is also very clear. In this pre-request, the PUT method is not allowed to be used in cross-domain. We need to change the configuration of Access-Control-Allow-Methods (what is missing, here I only add If you have PUT, you can add it all by yourself), let the browser know that the server is allowed

 server {
    listen 22222;
    server_name localhost;
    location / {
        if ($request_method = 'OPTIONS') {
            add_header Access-Control-Allow-Origin 'http://localhost:8080';
            add_header Access-Control-Allow-Headers 'content-type,authorization';
            add_header Access-Control-Allow-Methods 'PUT';#为这么只加在这个if中,不再下面的if也加上?因为这里只有预检请求会校验,当然你加上也没事。
            return 204;
        }
        if ($request_method != 'OPTIONS') {
            add_header Access-Control-Allow-Origin 'http://localhost:8080' always;
        }
        proxy_pass http://localhost:59200;
    }
}

Note here that after changing to the PUT type, the Access-Control-Allow-Headers request response header will automatically verify the content-type request header, which is the same as in case 3. Just make up for whatever is missing. If content-type is not added, the following error will be reported. (If you want to keep it simple, Access-Control-Allow-Headers and Access-Control-Allow-Methods can be set to *, which means they all match. But Access-Control-Allow-Origin is not recommended to be set to *, for security reasons, restrictions A domain name is necessary.)

图片

After adding all of them, the problem is solved. The 405 reported here is that the interface on my server side only opens GET, not PUT. At this moment, I use the PUT method to request this interface, so the interface will return this status code.

图片

Case 5:

Finally, let's talk about another situation, that is, the back-end handles cross-domain, so you don't need to handle it yourself (here, some back-end engineers change the server-side code to solve cross-domain, but they don't understand the principle, so they can find it on the Internet. If the code is pasted, the response information may not be processed completely. For example, the method is not fully added, and the headers are not added to the point. The one you use may not be copied from the actual project, and the options request is not added to return the status code. etc., causing Nginx to use the general configuration again, the following exception may be reported)

 Access to XMLHttpRequest at 'http://localhost:22222/api/Login/TestGet' from origin 'http://localhost:8080' has been blocked by CORS policy: The 'Access-Control-Allow-Origin' header contains multiple values '*, http://localhost:8080', but only one is allowed.

图片

图片

It means that there are multiple Access-Control-Allow-Origin request response headers returned at the moment, and only one is allowed. In this case, of course, modify the configuration to remove the Access-Control-Allow-Origin configuration, but in this case , it is recommended to choose only one of Nginx configuration and server-side solution for cross-domain. (Note here that the Access-Control-Allow-Origin in if $request_method = 'OPTIONS' cannot be deleted if you follow my writing above, just delete !='OPTIONS', because if it is a preflight request here If it is turned directly, the request will not be forwarded to the 59200 service. If it is also deleted, it will report the same error as in case 1. So why do you say that the cross-domain solution should not be solved at the server-side code level, or it should be solved by Nginx proxy? Mix it up, otherwise people who don't understand the principle will probably not be able to solve the problem by looking for a piece of code posted on the Internet)

Then paste a complete configuration (* fill in according to your 'preference'):

 server {
        listen       22222;
        server_name  localhost;
        location  / {
            if ($request_method = 'OPTIONS') {
                add_header Access-Control-Allow-Origin 'http://localhost:8080';
                add_header Access-Control-Allow-Headers '*';
                add_header Access-Control-Allow-Methods '*';
                add_header Access-Control-Allow-Credentials 'true';
                return 204;
            }
            if ($request_method != 'OPTIONS') {
                add_header Access-Control-Allow-Origin 'http://localhost:8080' always;
                add_header Access-Control-Allow-Credentials 'true';
            }
            proxy_pass  http://localhost:59200; 
        }
    }

or:

 server {
        listen       22222;
        server_name  localhost;
        location  / {
            add_header Access-Control-Allow-Origin 'http://localhost:8080' always;
            add_header Access-Control-Allow-Headers '*';
            add_header Access-Control-Allow-Methods '*';
            add_header Access-Control-Allow-Credentials 'true';
            if ($request_method = 'OPTIONS') {
                return 204;
            }
            proxy_pass  http://localhost:59200; 
        }
    }

Finally, this is a process of solving cross-domain problems. If you read it carefully, I believe it should be easy to understand and solve the problem in actual use. I hope it can help everyone. The above content They are all based on their own understanding of the test code. If there is something wrong with the understanding, please correct me.

Source: cnblogs.com/fnz0/p/15803011.html

If this article is helpful to you, please like & forward it, it is very important for me to continue to share & create high-quality articles. Thanks 🙏🏻


民工哥
26.4k 声望56.7k 粉丝

10多年IT职场老司机的经验分享,坚持自学一路从技术小白成长为互联网企业信息技术部门的负责人。2019/2020/2021年度 思否Top Writer