nginx 新手,最近碰到一个很奇怪的问题,甚至公司前端大佬都表示疑惑哈哈,最后翻官方文档解决了,所以记录一下。
背景
我现在有一个用 CRA 新建的 react web 项目,后端是用的 .net core web api 写的。
当然,这中间涉及跨域问题,毕竟网站的 URL 和 API 的 URL 是不一样的。
所以我的解决方法是这样,如果是在本地开发环境,直接在 Program.cs
里面写明:
if (app.Environment.IsDevelopment())
{
app.UseCors(options => options
.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod()
);
}
这样实际上本地就不存在跨域问题了。
而线上,我是使用的 windows server + iis + nginx 的搭配,所以跨域的东西当然要写在 nginx.conf
里面。
当然,只用写 API 的 server 就好,不用写网站的 server。
以其中一个环境的 API 的 server 为例,代码如下:
server {
listen 80;
server_name env.api.domain.com;
location / {
add_header "Access-Control-Allow-Origin" "http://env.web-url.domain.com";
add_header "Access-Control-Allow-Methods" "*" always;
add_header "Access-Control-Allow-Headers" "*" always;
if ($request_method = 'OPTIONS') {
return 204;
}
proxy_pass http://localhost:1001;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
看起来是没有什么问题的。
问题表现
接下来有个奇怪的问题。这个问题表面上是跟用户的 token 是否过期有关的。
token 未过期 | token 已过期 | |
---|---|---|
本地环境 | API 200,正常 | API 401,正常 |
线上环境 | API 200,正常 | API 401,跨域报错 |
没错,就是这么离谱。在线上环境,token 如果过期再请求 API,则浏览器跨域报错,但服务器成功返回了 401。
这就会导致前端的 fetch 直接报错 failed to fetch,而不会进入到 response 更不会有 response.status 是不是等于 401。
在 Chrome 里的表现是,Console 报错:
- Access to fetch at ‘[my-api-url]’ from origin ‘[my-web-url]’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource. If an opaque response serves you needs, set the request’s mode to ‘no-cors’ to fetch the resource with CORS disabled.
- GET [my-api-url] net::ERR_FAILED 401 (Unauthorized)
Network 正常:
- [my-api-url-suffix] OPTIONS 204
- [my-api-url-suffix] GET 401
是不是感觉很离谱……这问题我找了一天多跟 Chat GPT 也 battle 了一天多硬是没找到结果。
头都大了。
解决方案
解决方案超级简单。
根据上面的表格,基本可以断定问题八成出在 nginx 上。
你不是找不到 Access-Control-Allow-Origin 么?那就是没有加上呗。
所以解决方案就是,把 nginx.conf
里的这一行:
add_header "Access-Control-Allow-Origin" "http://env.web-url.domain.com";
改成同添加其他 headers 一样的逻辑,在最后面添加一个 always
:
add_header "Access-Control-Allow-Origin" "http://env.web-url.domain.com" always;
这个 always
有什么用呢?
根据 nginx 官方文档 解释,nginx 仅当 status code 为 200, 201, 204, 206, 301, 302, 303, 304, 307, 308 时,才会添加指定的 headers。除非你添加了 always
标识,才会不分 status code 都添加 header。
Adds the specified field to a response header provided that the response code equals 200, 201 (1.3.10), 204, 206, 301, 302, 303, 304, 307 (1.1.16, 1.0.13), or 308 (1.13.0). Parameter value can contain variables. There could be several add_header directives. These directives are inherited from the previous configuration level if and only if there are no add_header directives defined on the current level. If the always parameter is specified (1.7.5), the header field will be added regardless of the response code.
问题解决。