跨域是服务器拒绝请求还是浏览器去拒绝的请求?

这是一个非常好的问题!简单来说:跨域限制是浏览器主动实施的,服务器可以参与控制这个过程,但最终是浏览器决定是否拒绝请求。

让我详细解释一下:

1. 浏览器是执行者

  • 跨域请求(CORS - Cross-Origin Resource Sharing)限制是浏览器的安全策略
  • 当你从一个源(如 https://example.com)的网页中向另一个源(如 https://api.other.com)发送AJAX/fetch请求时,浏览器会自动:
    1. 检查请求是否是"简单请求"还是"非简单请求"
    2. 对于非简单请求,会先发送一个预检请求(OPTIONS请求)
    3. 根据服务器返回的CORS头部决定是否允许实际请求

2. 服务器的角色

服务器通过响应头告诉浏览器:

  • Access-Control-Allow-Origin:允许哪些源访问
  • Access-Control-Allow-Methods:允许哪些HTTP方法
  • Access-Control-Allow-Headers:允许哪些请求头
  • Access-Control-Allow-Credentials:是否允许发送凭据(如cookies)

3. 拒绝请求的场景

情况A:浏览器直接拒绝(没有CORS配置时)

javascript 复制代码
// 假设当前页面是 https://example.com
fetch('https://api.other.com/data')  // 不同域
  .then(...)
// 浏览器会直接报错:Cross-Origin Request Blocked
// 请求甚至没有到达 api.other.com

情况B:服务器拒绝,浏览器执行拒绝

javascript 复制代码
// 服务器返回的响应头:
// Access-Control-Allow-Origin: https://allowed.com
// 而当前源是 https://example.com
// 浏览器检查后发现不被允许,拒绝访问响应数据

4. 例外情况

  • 非浏览器环境:Node.js、Postman、curl等工具没有跨域限制
  • 服务端到服务端:服务器之间的请求不受CORS限制
  • 某些HTML标签<img><script><link>等可以跨域加载资源

关键要点

  • 浏览器主动实施:跨域是浏览器的安全策略
  • 服务器被动响应:服务器通过响应头告诉浏览器自己的跨域策略
  • 协作机制:浏览器检查服务器的CORS头部后决定是否允许请求继续

这就是为什么在开发时,前端遇到跨域问题需要后端配置CORS头部,或者在开发环境中使用代理服务器来解决跨域问题。

5. 时序图说明

这是一个跨域请求的时序图,展示浏览器、前端代码和服务器之间的交互流程:

sequenceDiagram participant F as 前端代码
(https://example.com) participant B as 浏览器 participant S as 服务器
(https://api.other.com) Note over F,S: 1. 简单请求(无预检) F->>B: 发送fetch()请求
GET /api/data B->>S: 直接发送请求 S-->>B: 返回响应 + CORS头
(Access-Control-Allow-Origin) B->>B: 检查CORS头
当前源是否在允许列表中? alt CORS允许 B-->>F: 返回响应数据 else CORS拒绝 B-->>F: 抛出跨域错误
响应被浏览器屏蔽 end Note over F,S: 2. 非简单请求(有预检) F->>B: 发送fetch()请求
PUT /api/data + 自定义头 B->>S: 发送OPTIONS预检请求 S-->>B: 返回预检响应
包含CORS策略头 B->>B: 检查预检响应 alt 预检通过 B->>S: 发送实际PUT请求 S-->>B: 返回实际响应 + CORS头 B-->>F: 返回数据 else 预检拒绝 B-->>F: 直接报错
实际请求不会发送 end

时序流程说明:

第一阶段:前端代码发起请求

  1. 前端JavaScript调用fetch()XMLHttpRequest
  2. 浏览器接收到请求指令

第二阶段:浏览器判断请求类型

bash 复制代码
简单请求条件(同时满足):
- 方法:GET、POST、HEAD
- 头部:仅限安全头部(Accept、Accept-Language等)
- Content-Type:text/plain、application/x-www-form-urlencoded、multipart/form-data

第三阶段:分叉处理

A. 简单请求路径:

  • 浏览器直接发送请求到服务器
  • 服务器返回响应和CORS头部
  • 浏览器检查CORS头部,决定是否将响应给前端代码

B. 非简单请求路径(需要预检):

diff 复制代码
触发预检的情况:
- 方法:PUT、DELETE、PATCH等
- 自定义头部:X-Custom-Header等
- Content-Type:application/json等
  1. 浏览器先发送OPTIONS预检请求
  2. 服务器返回CORS策略
  3. 关键决策点 :浏览器检查策略
    • 通过 → 发送实际请求
    • 拒绝 → 直接报错,不发送实际请求

第四阶段:最终结果

  • 成功 :数据传递给前端代码的then()onload
  • 失败 :触发catch()onerror,控制台显示跨域错误

关键决策点时序:

graph TD A[前端发起请求] --> B{浏览器判断
简单请求?} B -->|是| C[直接发送请求] B -->|否| D[发送OPTIONS预检] D --> E{服务器返回
CORS策略} E -->|允许| F[发送实际请求] E -->|拒绝| G[直接报错] C --> H[服务器返回响应] F --> H H --> I{浏览器检查
CORS头} I -->|源允许| J[传递数据给前端] I -->|源拒绝| K[屏蔽响应并报错]

实际报错示例时间点:

  • 预检阶段失败:服务器未返回正确的OPTIONS响应
  • 响应阶段失败Access-Control-Allow-Origin不匹配当前源
  • 凭证请求失败 :设置了credentials: 'include'但服务器未返回Access-Control-Allow-Credentials: true

这个时序图清晰地展示了跨域限制是浏览器主动实施的过程,服务器只是被动地响应浏览器的询问(通过CORS头部)。

相关推荐
重铸码农荣光1 小时前
深入理解 JavaScript 中的 this:一场关于作用域、调用方式与设计哲学的思辨
前端·javascript
珑墨1 小时前
【包管理器】pnpm、npm、cnpm、yarn 深度对比
前端·javascript·npm·node.js
草字1 小时前
uniapp 滚动到表单的某个位置,表单验证失败时。
前端·javascript·uni-app
学到头秃的suhian1 小时前
Spring使用三级缓存解决循环依赖问题
前端·spring·缓存
CXH7281 小时前
架构师的登山之路|第十二站:服务网格 Istio——未来的标配,还是复杂过头?
前端·javascript·istio
脾气有点小暴1 小时前
详解 HTML Image 的 mode 属性:图像显示模式的灵活控制
前端·html·uniapp
0思必得02 小时前
[Web自动化] 开发者工具性能(Performance)面板
运维·前端·自动化·web自动化·开发者工具
心灵的制造商2 小时前
el-tree左侧新增类别和删除类别实例代码
前端·javascript·vue.js
冴羽2 小时前
不知道怎么写 Nano Banana Pro 提示词?分享你一个结构化示例,复刻任意图片
前端·人工智能·aigc