云主机跨域问题如何处理?
- 来源:纵横数据
- 作者:中横科技
- 时间:2026/5/20 15:39:40
- 类别:新闻资讯
前阵子一个做在线发票系统的朋友小孙找到我,说他们新上线的一个功能出了大问题。这个功能是在网页里调用另一个云主机上的API接口获取企业开票数据,开发的时候在测试环境跑得好好的,一部署到正式环境就报错。浏览器控制台里赫然躺着一行红字,大概意思是说从某个源地址发起的请求,被跨域资源共享策略给拦截了,请求头里的字段不在许可列表里。小孙说他看了半天也没明白,明明两台云主机都是他自己名下的,域名也都是他自己的,怎么就不让访问了呢。
我跟小孙说,这个问题其实特别典型,几乎所有从零开始做前后端分离的项目,都会在某个时刻撞上跨域这道墙。跨域不是你的代码写错了,也不是你的云主机出了故障,而是浏览器为了保护用户安全而设置的一道关卡。理解了这个逻辑之后,你会发现处理跨域问题不仅不难,反而有很多灵活的办法可以应对。
我先用大白话解释一下什么是跨域。你在浏览器里打开了一个网页,这个网页的地址是什么,比如是网站A,然后这个网页里的JavaScript代码想去访问网站B上的资源,网站A和网站B的域名不一样,或者端口不一样,或者协议不一样,浏览器就会说,不行,你不能随便去别的地方拿东西,这叫同源策略。同源策略的本意是好的,防止恶意网站偷走你的登录信息或者操作用户的账户。但现实中的很多应用确实需要不同域名之间的正常通信,比如你的前端页面部署在一台云主机上,后端接口部署在另一台云主机上,或者你的网页调用了第三方提供的API服务。为了解决这个矛盾,浏览器提供了一个机制叫做跨域资源共享,就是让服务器可以主动告诉浏览器,哪些源可以来访问我,哪些请求方法可以调用我,哪些请求头可以发送。你只需要在云主机上把响应头配置好,浏览器检查通过之后就会放行请求。
小孙遇到的情况就是典型的跨域配置缺失或者配置不正确。下面我结合处理过的各种案例,把跨域问题的排查和解决方法详细说一下。
第一类问题也是最常见的问题,就是服务端根本就没有配置跨域相关的响应头。很多后端开发人员不太关注这块,写的接口在本地调试的时候用的是同一个域名同一个端口,所以从来没触发过跨域限制。一部署到云主机上,前端和后端分开了,问题就暴露了。解决这个问题的核心就是让后端服务在返回响应的时候带上特定的响应头,这个头告诉浏览器,允许来自哪些源的请求。如果你希望你的API能被所有来源正常调用,可以把值设置为星号,表示允许任何域名访问。但这里有个细节,当你需要携带用户登录凭证的时候,比如Cookie或者认证头信息,星号就不行了,你必须明确写上前端页面的具体域名。很多人在这里翻车,明明后端配置了允许跨域,但前端请求还是被拦截了,多半是这个原因。
我帮小孙排查的时候,先用开发者工具看了他API接口的响应头,发现里面根本就没有跨域相关的头部。这意味着他的后端服务完全没有开启跨域支持。他用的是一种比较常见的后端框架,我告诉他这个框架里开启跨域支持的方法有好几种,最直接的是在代码里加一个中间件,这个中间件会在每个响应里自动加上所需的头信息。他照着文档改了代码,重新部署之后,响应头里终于有了。
第二类问题是请求方法不被允许。浏览器在发送某些类型的跨域请求之前,会先发送一个探测性的请求,这个探测请求用的是特定的方法,目的是问服务器,你允许我使用这个请求方法吗,你允许我发送这些自定义头吗。如果服务器对探测请求的响应里没有明确允许这个请求方法,浏览器就会拦截真正的请求。比如你想用PUT方法去更新数据,但服务器只在响应里标注了允许GET和POST,那么PUT请求就会被拒绝。解决的方法是在服务器的跨域配置里,加上允许的方法列表,把你需要用到的方法都加进去。
说一个真实案例。我认识一家做在线文档协作的公司,他们的编辑器需要把用户写的内容用PUT方法保存到服务器上。正式环境上线之后,一直有用户反馈保存失败,但刷新一下又好了,非常诡异。后来抓包一看,那些保存失败的请求,浏览器先发了一个探测请求,服务器探测请求响应里允许的方法列表写的是星号,理论上应该没问题。但他们的云主机前面挂了一个老旧的代理服务器,这个代理服务器不支持星号这种通配符的写法,强行把星号理解成了没有允许任何方法,于是一个本来应该通过的请求就这么被挡住了。解决的办法是把星号改成明确的方法列表,代理服务器认出了这些方法名称,问题就解决了。这个案例告诉我们,有时候问题不在你直接配置的那个软件上,而在于整个链路上的某一个环节。
第三类问题是请求头不被允许。前端在发送请求的时候,有时候会携带一些自定义的头部信息,比如用来做认证的特定头部,或者用来标识客户端版本的特定头部。对于跨域请求来说,如果这些自定义头不在服务器允许的列表里,浏览器同样会拦截。服务器需要在探测请求的响应里加上允许请求头的头部,把前端会用到的自定义头名称写进去,多个头部之间用逗号隔开。还有一种情况是前端发送了标准的头但值比较特殊,比如头部里放了非ASCII字符,或者Content-Type用了非标准的值,这些也可能触发浏览器的预检,如果服务器没明确允许,同样会报错。
第四类问题是跨域配置没有覆盖到错误响应。这个问题非常隐蔽,我见过不少开发团队在这里吃过亏。正常情况下的跨域配置没有问题,但一旦后端接口报错,比如返回了4xx或者5xx状态码,某些后端框架的跨域中间件在这个阶段就停止执行了,错误响应返回的时候没有带上跨域相关的头。浏览器收到这个没有跨域头的错误响应,按照安全策略直接拒绝把响应内容交给页面的JavaScript代码。也就是说,前端只知道请求失败了,但看不到具体的错误状态码和错误信息,给调试带来了很大困扰。解决这个问题需要确保跨域中间件在任何情况下都会执行,包括在异常处理流程中。有些框架提供了专门的异常处理钩子,你需要在那个钩子里也加上跨域头的设置。
第五类问题是跨域配置重复或者冲突。有些项目里,跨域配置可能被放在了多个地方,比如在代码层面配置了一次,在云主机前面的反向代理上又配置了一次,这两处的配置如果设置的值不一样,就可能产生冲突。浏览器收到的响应头里如果出现了两个同名的头部,虽然规范允许这种做法,但某些浏览器版本可能会按自己的规则处理,导致结果跟预期不符。更麻烦的是,有时候两处配置的允许来源列表不一致,一个允许具体的域名,另一个允许星号,浏览器会采用哪一个呢,行为是不可预测的。所以建议跨域配置只在一个地方做,要么在代码层面,要么在反向代理层面,不要两边都做。
我处理过一个比较极端的案例。一个电商平台的开发团队在云主机上配置了跨域,又在CDN上配置了跨域,还在负载均衡器上配置了跨域,三个地方的配置值还不完全一样,最后浏览器收到的响应头里出现了三组重复的头信息,每个头的值都不一样。浏览器的处理方式是取最后一个,但不同浏览器对重复头的处理规则有细微差别,导致有些用户正常,有些用户报错,排查起来非常痛苦。后来他们统一把跨域配置只放在后端代码里,彻底清除了其他地方的配置,问题就解决了。
第六类问题是预检请求本身被拦截了。浏览器发送的探测请求虽然不携带真正的业务数据,但它也是一个实实在在的HTTP请求。这个请求可能会被云主机的防火墙拦截,可能会被WAF拦截,也可能会被安全组规则拦截。因为探测请求用的是特定的方法,有些安全策略默认会拦截所有非标准方法。如果你发现浏览器控制台里没有报跨域的错误,而是直接显示网络请求失败,那就要考虑探测请求根本没到达后端服务。排查的方法是查看云主机上的访问日志,看在探测请求发生的时间点有没有对应的记录,如果没有,说明请求在路上就被拦截了。
第七类问题是前端代码层面的问题。有时候不是服务端配置错了,而是前端请求的方式有问题。比如前端在请求里设置了一些浏览器不允许自定义的头部字段,或者使用了不支持的请求方法,这些都属于前端代码的问题,跟跨域配置无关。还有一个常见的前端错误是在用fetch发送请求的时候,没有正确设置一个必要的选项,导致本应携带Cookie的跨域请求变成了不带Cookie的请求,服务器因为没有收到认证信息而返回未授权错误,看起来像跨域问题,实际上是认证问题。这类问题需要在浏览器开发者工具的Network面板里仔细看实际发出的请求和收到的响应,不要一看到红色报错就断定是跨域问题。
第八类问题是云主机上部署了多个服务,服务之间的调用路径比较复杂。比如前端页面请求的是网关的地址,网关再转发到后端的各个微服务。跨域配置需要在网关这一层做,而不是在各个微服务上分别做。如果错误地在微服务上做了跨域配置,但网关没有做,那么前端请求到达网关的时候可能就被拦截了。反过来也一样,如果只在网关上做了配置,但微服务之间的内部调用没有走网关,而是前端直接调用某个微服务的地址,那跨域问题依然存在。所以要理清整个请求链路,确定浏览器实际访问的是哪个地址,就在那个地址上做跨域配置。
小孙最后把所有问题解决之后,我建议他做一件事,就是把他API接口的跨域配置写成一个标准的模板,包括允许的源列表、允许的方法列表、允许的请求头列表、允许携带凭证的设置,还有预检请求的缓存时间。这个模板放在公司的代码仓库里,以后任何新项目需要用到跨域的时候,直接复制这个模板进去,只要做很少的修改就能用。这样可以避免每次都要重新摸索配置参数。
总结一下,云主机跨域问题的处理,核心思路是先确认问题是不是真的跨域问题,也就是看浏览器的报错信息里有没有相关的关键词。然后检查服务端是否返回了正确的响应头,包括允许的源、允许的方法、允许的请求头。如果请求需要携带Cookie,还要特别注意源的配置不能是星号,必须是明确的域名。对于带预检的复杂请求,要确保探测请求的响应里把需要的方法和头都明确列出来。注意跨域配置的生效范围,确保正常响应和错误响应都能带上正确的跨域头。整个链路上只在一个地方做跨域配置,避免重复配置导致冲突。最后,如果所有配置都检查过了还是不行,回头看前端的请求代码,确认请求本身没有违反浏览器的基本规则。
跨域问题在开发过程中几乎是绕不开的,但它不是什么高深莫测的难题,本质就是浏览器和服务器之间的一套握手约定。你只要理解了这套约定的规则,配置起来其实很直观。




使用微信扫一扫
扫一扫关注官方微信 

