June 18, 2024, 3:16 p.m. by lufy
This is the most useful post I've read when I met a CORS problem.
Refrinted from https://medium.com/@hubian/nginx-easily-solves-cross-domain-issues-009b95a5185e
When you encounter a cross-domain problem, don’t immediately choose to copy it and try it. Please read this article in detail before processing. I believe it can help you.
Preparation before analysis:
First, make sure that the server does not handle cross-domain issues. Second, use postman to test whether the server interface is normal.
When the website 8080 accesses the server interface, a cross-domain problem occurs. So how to solve it? Next, I will list various situations encountered across domains and solve them through nginx proxy (the same is true in the background, as long as you understand the principle).
Cross-domain mainly involves 4 response headers:
Access-Control-Allow-Origin
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 information 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)Access-Control-Allow-Credentials
Whether to allow the use of cookies across domains. If you want to use cookies across domains, you can add this request response header and set the value to true (setting it or not setting it will not affect the sending of the request, but will only affect whether cookies should be carried across domains. , but if set, both preflight requests and formal requests need to be set). However, it is not recommended to use it cross-domain (it has been used in the project, but it is unstable and cannot be carried by some browsers) 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 where cross-domain problems will still be reported even if the configuration is correct. .
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 information fields can be used. Only when a positive reply is received will the browser issue a formal XMLHttpRequest
request, otherwise an error will be reported. As shown below
Get started with the simulation:
Nginx proxy port: 22222, configured 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 Nginx proxy port 2222. You can see that the interface can be accessed normally after passing the proxy as shown below.
Next, use website 8080 to access the interface address behind the Nginx proxy. The error message 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 description is a pre-request. The CORS mechanism cross-domain will first perform a preflight (an OPTIONS request), and then the real request will be sent after the request is successful. This is designed to ensure servers are aware of the CORS standard to protect older servers that do not support CORS
Through the error message, we can know that the request response header of the preflight request is missing Access-Control-Allow-Origin
. If there is an error, we can just change it. Modify the Nginx configuration information as follows (the red part is the added part), fill in whatever 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 with Nginx.
add_header
The directive is used to add return header fields and is valid if and only if the status code is those listed in the figure. If you want every response message to carry header field information, you need to add always at the end (after my testing, only Access-Control-Allow-Origin
this header information needs to be added with always, and other header information will be carried back without adding always), then let's try adding it.
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 took effect. Of course, it is not a cross-domain problem. The above problem has been solved because the error content 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 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;
}
}
After the configuration was completed, I found that the error message had 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.
This means that the authorization header information is missing in the pre-request response header Access-Control-Allow-Headers
(each situation will be different. After cross-domain occurs, the custom header information added is not allowed and needs to be added to the request response header Access-Control-Allow-Headers
so that the browser knows this The carrying of header information is recognized by the server as legal. What I carry here is authorization. Others may be tokens and the like. Add whatever is missing). Once you know the problem, then modify the configuration file, add the corresponding missing part, and try again. try
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 was found that the error reporting problem returned to situation 1.
After testing, it has been verified that as long as if ($request_method = 'OPTIONS')
it is written inside add_header
, the external configuration will be invalid when making 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.
This means that add_header
when there are no instructions at the current level, the ones from the previous level will be inherited add_header
. On the contrary, if the current level exists add_header
, it should not be able to inherit from the previous level add_header
.
The configuration modification is 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, after the modification, I found that the cross-domain problem has been solved.
However, although the above solves the cross-domain problem, considering that the Nginx version may be updated in the future, I don’t know whether this rule will be modified. Considering that this writing method may carry two Access-Control-Allow-Origin
, this situation is not allowed, as will be discussed below. arrive. So the configuration should be 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, keep chatting↓↓
Case 4:
Earlier APIs may only use POST and GET requests, and Access-Control-Allow-Methods
this request response header only supports POST and GET across domains by default. When other request types occur, cross-domain exceptions will also occur.
For example, I changed the requested API interface request method from the original GET to PUT and tried it again. On the console an error will be 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 error content 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 Access-Control-Allow-Methods
configuration (is there anything missing? I only added PUT here. You can add it yourself. point) to 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 PUT type, Access-Control-Allow-Headers
the request response header will automatically verify content-type
the request header, which is the same as case 3. Just make up for whatever is missing. If not added content-type
, the following error will be reported.
If you want to make it simple, Access-Control-Allow-Headers
and Access-Control-Allow-Methods
can be set to *
, which means they all match. However, Access-Control-Allow-Origin
it is not recommended to set *
it up . For security reasons, it is necessary to restrict domain names.
After adding them all, the problem is solved. The 405 reported here is that the interface on my server only opens GET and does not open 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 backend handles cross-domain, and there is no need to handle it by yourself (here is a complaint, some backend engineers change the server code to solve cross-domain, but they don’t understand the principle, so they can find it on the Internet. The code snippet is pasted, resulting in incomplete processing of the response information. For example, the method is not added completely, the headers are not added to the point, the one you use may not contain the ones used in the actual project, and the options request is not returned with a status code. etc., causing Nginx to report the following exception if it reuses the common configuration)
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.
This means that Access-Control-Allow-Origin
multiple request response headers are returned at this moment, but only one is allowed. In this case, of course, you can modify the configuration and remove Access-Control-Allow-Origin
this configuration. However, in this case, it is recommended that Nginx configuration and the server solve the cross-domain problem by themselves and only select other one.
Note here that if you follow the way I wrote it above, you can’t delete if $request_method = 'OPTIONS'
the ones inside Access-Control-Allow-Origin
. Just delete !='OPTIONS'
the ones inside, because if it is a preflight request, it will be returned directly, and the request will not be forwarded to the 59200 service. If it is also deleted, it will The same error as case 1 will be reported. So why is it said that cross-domain should be solved at the server code level, or Nginx proxy should be used to solve it? Don’t mix it up, otherwise people who don’t understand the principle may not be able to solve the problem by looking for a piece of code posted online.
Post a complete configuration again ( *
fill in the number according to your own '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 an article about the process of solving cross-domain problems. If you read it carefully, I believe you can easily understand it and solve the problem yourself in actual use. I hope it can help everyone. The above content I came up with my own understanding of my own test code. If I understand anything wrong, please correct me.
Comment is not approved to this article.