接口在自己的域名下测得好好的,前端一调就红。控制台那行字是No Access-Control-Allow-Origin header is present。后端同事说头已经加了,还截了图。两边都没说谎,问题在中间。这类报错几乎每次都要重新推一遍流程,因为位置不同。
先看Network面板里是不是有一条OPTIONS请求。带自定义头、带JSON的POST,浏览器会先发一次预检,通过了才发真正的请求。预检没过,真正的请求根本没有发出去。
预检失败最常见的原因是它压根没走到业务代码。nginx里没有为OPTIONS配规则,请求被当成普通请求处理,路由不认这个方法,直接404。404的响应由nginx自己生成,业务代码里的头一个都不会有。还有一种更绕的,框架的路由表里只注册了GET和POST,OPTIONS直接抛405,中间件根本没机会跑。curl -X OPTIONS -i试一下,返回404或者405,答案就在这儿。这一步用浏览器看也一样,Network里那条红色就是了。
第二个坑是带cookie的时候,Access-Control-Allow-Origin不能写星号。浏览器只接受一个具体的域名,而且Access-Control-Allow-Credentials必须是true。写成星号的话,控制台会明确说不能和credentials一起用。还有人用正则回显Origin,忘了处理换行和空格,回显出来的值带了一个尾空格,浏览器判无效。回显是对的写法,但一定要白名单,把允许的域名列出来,别把请求里带来的Origin原样返回,那等于谁都能跨。这一层顺手把Vary: Origin也加上,不然中间缓存会串。
第三个坑是头被设了两遍。nginx的add_header加一次,PHP里又header加一次,响应里就出现两条同名头,值不一样。浏览器看到两个不同的值,直接拒绝。
第四个坑在错误页上。接口超时返回504,这个页面是nginx生成的,业务代码一行没跑,自然也没有跨域头。前端看到的报错是跨域,实际原因是超时,方向就带偏了。遇到CORS报错,先看状态码,再看头。5xx的响应头本来就少,别指望它有。还有一种502,是php-fpm挂了,同样没有头,日志里能看到upstream prematurely closed。
你要是用curl测,永远测不出跨域问题。curl不管同源策略,服务端返回什么它就收什么,头缺了也照收。所以命令行里一切正常,浏览器里一直红,这两件事不矛盾。要复现得带Origin头,再加一个-X OPTIONS。这也是为什么后端自测永远通过。
现在这套是nginx里统一给OPTIONS返回204,把三个头一次配齐,业务代码不再碰。配在server层,location里不要再写add_header,写了会覆盖。add_header有个坑,下级块里一旦出现它,上级全部失效,不是合并,是替换。这一条比什么排查技巧都省事。
A5创业网 版权所有