Hono 前后端分离系列 04:CORS、环境变量和前端联调
前后端分离项目里,第一个让新人怀疑人生的问题,通常不是数据库,也不是鉴权。
而是 CORS。
页面明明能打开,接口明明能单独访问,前端一请求就报错:
1 | Access to fetch at ... has been blocked by CORS policy |
这不是 Hono 的问题,也不是浏览器抽风。
这是浏览器在保护用户。
这篇文章把 Hono 项目里的 CORS、环境变量和前端联调讲清楚。这个基础打不好,后面 JWT、cookie、文件上传、部署都会继续出问题。
一、为什么本地会跨域
本地开发常见结构:
1 | 前端:http://localhost:5173 |
协议一样,域名一样,但端口不同。
浏览器判断 origin 时看三件事:
1 | 协议 |
只要有一个不同,就是不同源。
所以 localhost:5173 请求 localhost:8787,就是跨域。
服务器如果没有明确允许,浏览器就会拦截响应。
注意,是浏览器拦截。
你用 curl 或 Postman 能访问,不代表前端页面也能访问。
二、Hono 里启用 CORS
Hono 内置了 CORS middleware。
最简单的写法:
1 | import { Hono } from 'hono' |
Hono 官方文档提醒,CORS middleware 应该在 route 之前调用。
也就是说:
1 | app.use('/api/*', cors()) |
不要反过来。
三、开发环境不要直接放开生产环境
很多教程会这样写:
1 | app.use('*', cors()) |
这在本地测试很方便,但生产环境不能无脑照搬。
更合理的方式是按环境配置 origin:
1 | const app = new Hono<{ |
如果你跑在 Node.js,也可以从环境变量读取:
1 | const corsOrigin = process.env.CORS_ORIGIN ?? 'http://localhost:5173' |
生产环境应该明确允许你的前端域名:
1 | https://app.example.com |
而不是随手 *。
四、credentials 是另一条规则
如果你用 cookie 保存登录态,前端请求通常要带:
1 | fetch('/api/me', { |
这时后端 CORS 也要允许 credentials:
1 | app.use('/api/*', cors({ |
这里有个关键点:
1 | credentials: true 时,不要使用 origin: '*' |
浏览器不会接受这种组合。
如果你用的是 Bearer Token 放在 Authorization header 里,不一定需要 credentials。但如果要发送 cookie,就必须同时处理:
- CORS credentials
- Cookie SameSite
- Cookie Secure
- HTTPS
前后端分离里,cookie 登录比 token header 更容易踩坑。
五、前端环境变量怎么配
前端不要把 API 地址写死在代码里。
Vite 项目可以这样配:
.env.development:
1 | VITE_API_BASE_URL=http://localhost:8787 |
.env.production:
1 | VITE_API_BASE_URL=https://api.example.com |
请求封装:
1 | const API_BASE_URL = import.meta.env.VITE_API_BASE_URL |
这样本地、测试、生产可以走不同 API 地址。
不要在代码里到处写:
1 | fetch('http://localhost:8787/api/users') |
后面改环境会很痛。
六、联调时怎么排查
遇到 CORS 问题,按这个顺序查。
第一,看请求是不是 OPTIONS 预检失败。
浏览器对复杂请求会先发 OPTIONS。比如带 Authorization header,或者 Content-Type: application/json 的某些请求,都可能触发预检。
第二,看响应头有没有:
1 | Access-Control-Allow-Origin |
第三,如果带 cookie,看有没有:
1 | Access-Control-Allow-Credentials: true |
第四,看 origin 是否精确匹配。
不要只看 URL 像不像,端口也算。
第五,确认 CORS middleware 放在 route 前面。
这几个点查完,80% 的 CORS 问题都能定位。
七、不要用代理掩盖问题
Vite proxy 很有用:
1 | server: { |
但它只是开发体验工具。
它会让浏览器以为请求仍然发给 localhost:5173,从而绕过跨域。
这不是坏事,但你必须清楚:
1 | proxy 能让本地舒服 |
上线前,一定要用真实前端域名请求真实后端域名测试一次。
八、最后的建议
CORS 不应该靠“试到不报错”为止。
我的建议是:
1 | 本地明确允许 localhost 前端端口 |
Hono 的 CORS middleware 已经足够好用。真正的问题通常是我们没把前端域名、部署域名、登录方式和环境变量想清楚。
前后端分离的联调质量,往往从 CORS 配置就能看出来。