4.2 KiB
4.2 KiB
tags, create time
| tags | create time | ||||
|---|---|---|---|---|---|
|
2026-04-29 15:30 |
localhost vs 127.0.0.1 —— 开发环境最常见的"伪同源"坑
概述
记录一个高频踩坑场景:前端访问 localhost,后端监听 127.0.0.1,浏览器却报了 CORS 错误。它们明明指向同一台机器,为什么不算同源?
正文
现象
页面: http://localhost:3000
请求: http://127.0.0.1:8080/api/user
结果: ❌ CORS blocked — "No 'Access-Control-Allow-Origin' header"
开发者百思不得其解:localhost 不就等于 127.0.0.1 吗?
真相:浏览器不认"等价",只认"相同"
浏览器的**同源策略(Same-Origin Policy)**判定标准是严格的字符串比较:
同源 = 协议完全相同 && 域名完全相同 && 端口完全相同
这里的"域名"是一个身份标识字符串,不做任何语义换算。localhost 和 127.0.0.1 是两个不同的字符串,所以浏览器认为这是两个不同的源。
思考:如果浏览器把
localhost:3000和127.0.0.1:3000视为同源,会有什么问题?
为什么浏览器要这样设计?
这不是 bug,而是有意为之的安全决策:
flowchart LR
A["域名 = 身份标识"] --> B["a.example.com ≠ b.example.com<br/>即使部署在同一服务器"]
A --> C["localhost ≠ 127.0.0.1<br/>即使解析到同一个网卡"]
B --> D["最小权限原则:<br/>网络可达 ≠ 安全可信任"]
C --> D
| 原因 | 说明 |
|---|---|
| 子域名隔离 | 同机运行多个子域名站点,不应互相读取对方数据 |
| Host Header 攻击面 | 如果有人伪造 Host 头写成 localhost,浏览器不能假设"IP 一样就是安全的" |
| 一致性保证 | 如果浏览器开始做"智能换算"(IP↔域名等价),规则会变得极其复杂且容易出漏洞 |
常见触发路径
前端 dev server (Vite/Webpack) → 默认 host = localhost:3000
↓
Node.js / Go / Python 后端 → 默认 bind = 127.0.0.1:8080(更安全)
↓
浏览器报 CORS 错误
| 框架 | 默认绑定地址 |
|---|---|
| Vite dev server | localhost |
| Webpack devServer | localhost |
Node.js listen() |
0.0.0.0(全接口,但也可能绑定 127.0.0.1) |
| Python Flask debug | 127.0.0.1 |
Gin (r.Run(":8080")) |
0.0.0.0(但显式写 r.Run("127.0.0.1:8080") 时就是 127.0.0.1) |
解决方法
方法一:统一域名(最推荐)
让前后端用同一个名称:
# 方式 A:全部用 localhost
前端: http://localhost:3000
后端: http://localhost:8080
# 方式 B:全部用 127.0.0.1
前端: http://127.0.0.1:3000
后端: http://127.0.0.1:8080
修改后端监听地址:
// Go — 显式绑定 localhost
r.Run("localhost:8080")
// Node.js / Express
app.listen(8080, "127.0.0.1") // 或 "localhost"
方法二:配置 CORS(正确姿势)
如果必须混用,在后端正确设置响应头:
origin := c.Request.Header.Get("Origin")
if origin != "" {
c.Header("Access-Control-Allow-Origin", origin)
}
c.Header("Access-Control-Allow-Credentials", "true")
详见 [[GIN/3-middleware/cors-registration-scope]]。
方法三:开发代理(最省事)
用 Vite proxy 转发请求,让它变成真正的同源:
// vite.config.js
export default {
server: {
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
}
}
}
}
请求流程变为:
浏览器 → localhost:3000/api/user (同源)
↓ Vite devServer 转发
localhost:8080/api/user
生活化类比:你家公司大门写着"大厦A座3层"(localhost),另一扇消防通道门贴着"3层回廊"(127.0.0.1)。虽然走到的是同一个办公室,但门禁系统只认牌匾上的名字——牌子不一样,就不给你刷卡。
关联笔记
- 跨域问题调试 — 完整的跨域排查指南
- GIN/3-middleware/cors-registration-scope — CORS 中间件注册方案
- GIN/3-middleware/cors-preflight — OPTIONS 预检请求机制