我的 VPS 上的设置如下:
我使用一个域名(我们称之为
https://example.com
),它通过 DNS A 记录重定向到我的 VPS IP。到目前为止一切顺利,前端正在我的域上运行。
我的nginx设置基本上是这些:
server {
server_name example.com www.example.com;
location / {
proxy_pass http://localhost:3000/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
加上 certbot 的东西。
我使用 js fetch() 请求来调用我的 golang REST API。请求的来源是
https://example.com
在我的后端 http 服务器中,我设置了以下标头来处理 CORS:
headers.Add("Access-Control-Allow-Credentials", "true")
headers.Add("Access-Control-Allow-Origin", "https:example.com")
headers.Add("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
if req.Method != http.MethodOptions {
next.ServeHTTP(rw, req)
return
}
// process an HTTP OPTIONS preflight request
headers.Add("Access-Control-Allow-Headers", "content-type, allow-credentials")
我还使用 autocert 包的管理器来处理 https,设置如下所示:
m := &autocert.Manager{
Cache: autocert.DirCache("secret-dir"),
Prompt: autocert.AcceptTOS,
Email: "[email protected]",
HostPolicy: autocert.HostWhitelist("example.com", "www.example.com"),
}
server := &http.Server{
// Addr: ":6060",
Addr: ":https",
TLSConfig: m.TLSConfig(),
// Set timeouts to avoid Slowloris attacks.
WriteTimeout: time.Second * 15,
ReadTimeout: time.Second * 15,
IdleTimeout: time.Second * 60,
Handler: routerWithCORS,
}
我的 GET(等)请求调用
https://127.0.0.1:6060/api/v1/whatever
,调用的来源是 https://example.com
,在浏览器的网络选项卡中我得到
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://127.0.0.1:6060/api/v1/me. (Reason: CORS request did not succeed). Status code: (null).
我还应用了这个开放的 nginx 设置来在 nginx 中启用 CORS,但没有成功:
location / {
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
#
# Custom headers and headers various browsers *should* be OK with but aren't
#
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
#
# Tell client that this pre-flight info is valid for 20 days
#
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain; charset=utf-8';
add_header 'Content-Length' 0;
return 204;
}
if ($request_method = 'POST') {
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range' always;
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;
}
if ($request_method = 'GET') {
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range' always;
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;
}
}
我以为我的 golang 后端不需要 https,因为请求来自 VPS 上的 127.0.0.1 并在同一 VPS 上调用 127.0.0.1:6060,但查看网络选项卡,调用的来源是https://example.com。所以我想这是主要问题。我无法从 https 源调用 http 资源。但即使在 https 中运行后端也不起作用。
我希望这个问题可能有一个不太复杂的解决方案,希望您能帮助我指明正确的方向。
通过在多个地方配置CORS,
你正在引发问题。 CORS 最多应配置在一个地方,只要它更容易并且配置更改更有可能得到审查。
那么应该在哪里配置CORS呢? 在 NGINX 等反向代理中配置 CORS 可能具有挑战性,因为您无法获得 Go 等编程语言的完整表达能力。这就是为什么我偏向于在应用程序级别配置 CORS。
通过手动设置响应标头来配置 CORS 很诱人,但很容易出错,尤其是在您不太熟悉该协议的情况下。你的具体案例证明了我的观点:
headers.Add("Access-Control-Allow-Origin", "https:example.com") headers.Add("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE") if req.Method != http.MethodOptions { next.ServeHTTP(rw, req) return } request headers.Add("Access-Control-Allow-Headers", "content-type, allow-credentials")
第一个语句不可能按预期工作,因为
https:example.com
不是有效的 Web 来源;你的意思是https://example.com
。
与普遍看法相反,您很可能不需要将
OPTIONS
列为允许的方法。
另一个问题是标头
Access-Control-Allow-Methods
在响应非预检请求时没有位置。
此外,并非所有
OPTIONS
请求都是预检请求。
所有这些困难应该足以让您相信,您最好依赖经过验证的 CORS 中间件库,它可以从您身上抽象出一些复杂性。
我最近发布了 jub0bs/fcors,一个 CORS 中间件库,其设计易于使用,而且重要的是,很难误用。这是一个例子:
cors, err := fcors.AllowAccessWithCredentials(
fcors.FromOrigins("https://example.com"),
fcors.WithMethods(
http.MethodGet,
http.MethodPost,
),
fcors.WithRequestHeaders("Content-Type"),
)
if err != nil {
log.Fatal(err)
}
// apply cors to your http.Handler
// start your server
(仅供参考,名为
cors
的结果具有类型 func(http.Handler) http.Handler
。)
调整此示例以满足您的需求,并删除 NGINX 配置中与 CORS 相关的所有内容。