Nginx easily solves cross-domain problems

When you encounter cross-domain problems, don't choose to copy immediately to try. Please read this article in detail before processing. I believe it can help you.

# Preparation before analysis:

Front-end website address: http://localhost:8080

Server URL: http://localhost:59200 

First, make sure that the server does not handle cross-domain. 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 all 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 official requests will be verified when cross-domain)

  • Access-Control-Allow-Headers The special header fields allowed to be carried across domains (only verified 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 with the value set to true (setting or not setting will not affect the sending of the request, but only in the Whether to carry cookies when cross-domain, but if set, both the pre-check request and the official request need to be set). However, cross-domain use is not recommended (it has been 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 header information directly to Nginx can solve the cross-domain problem. Of course, most of the cases can be solved, but I believe there are still many cases. It is clearly configured, and the cross-domain problem will also be reported. .

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 permission list, and which HTTP verbs and header fields can be used. Only when a positive reply is received, the browser will issue a formal XMLHttpRequest request, otherwise an error will be reported. As shown below

Start the hands-on simulation:

Nginx proxy port: 22222, configured as follows

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

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

Next, use the website 8080 to access the interface address behind the Nginx proxy, and the error report 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 (pay attention to the red part). The priflight indicates that it is a pre-request. The cross-domain CORS mechanism will first perform the preflight (an OPTIONS request), and the real request will not be sent until 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 lacks Access-Control-Allow-Origin. If there is a mistake, we can change it. Modify the Nginx configuration information as follows (the red part is the added part), fill in 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 was full of joy and thought it could be solved, I found that I still reported the same problem

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

 

The add_header directive is used to add a return header field, valid only if the status codes are those listed in the figure. If you want to carry the header field information for each response message, you need to add always at the end (after my test, only the Access-Control-Allow-Origin header information needs to be added with always, and other header information without always will be carried back), 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, I found that it has taken effect. Of course, it is not solved by cross-domain. The above problem has been solved, because the content of the error report 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.

You can know from the error message that the pre-request (option request) that is the default behavior of the cross-domain browser has not received the ok status code. At this time, modify the configuration file. When the request is an option request, return a status code 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 complete, 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, custom added header information is not allowed, and 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 by the server as legal. What I carry here is authorization, and others may be tokens. Add what is missing), and I know the problem. , 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 time, it is found that the error reporting problem has returned to situation 1

It has been tested and verified that as long as add_header is written in if ($request_method = 'OPTIONS'), the external configuration will be invalid when it is a preflight 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.

It means that when there is no add_header instruction in the current level, it will inherit the add_header of the previous level. On the contrary, if the current layer has add_header, it should not be able to inherit the add_header of the previous layer.

 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 time, the cross-domain problem has been solved after the modification.

However, although the above has solved the cross-domain problem, but considering that the Nginx version may be updated later, I don’t know if this rule will be modified. Considering that this writing method may carry two Access-Control-Allow-Origin, this is also the case It is not allowed, as will be mentioned below. Therefore, 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, let's continue chatting↓↓

# Case 4:

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

For example, here I change the request method of the requested API interface from the original GET to PUT, and try again after launching it. 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 (add what is missing, here I only add PUT, you can add it 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, and it is enough to make up for what is missing. If the content-type is not added, the following error will be reported. (If you want to be 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, limit A domain name is necessary.)

After all are added, the problem is solved. The 405 reported here is that the interface of my server is only open for GET, but not for 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 me 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 code to solve cross-domain by themselves, but they don’t understand the principle, just find it online A piece of code is pasted, resulting in incomplete processing of the response information. For example, if the method is not fully added, the headers are not added to the point, the one used by myself may not include the one used in the actual project, and the status code returned by the request without adding options Wait, causing Nginx to use common configurations may report the following exception)

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 at this moment, multiple Access-Control-Allow-Origin request response headers are returned, but 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 the server to solve the cross-domain problem by itself.

(Here, note that if you follow my writing method above, the Access-Control-Allow-Origin in if $request_method = 'OPTIONS' cannot be deleted, just delete !='OPTIONS', because if it is a pre-check request It will be turned directly, and 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 you should solve the cross-domain at the server code level, or use the Nginx proxy to solve it? Mix it up, otherwise people who don’t understand the principle may not be able to solve the problem by finding a piece of code on the Internet)

 ↓ ↓ ↓ ↓ ↓

Then post a complete configuration (the * number is filled in according to your own preferences):

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 you can solve the problem yourself in actual use. I hope it can help everyone. The above content It is all based on my own understanding of my own test code. If there is any wrong understanding, please correct me.

Guess you like

Origin blog.csdn.net/leonnew/article/details/123895626