问题 工作中,使用 nextJs 代理请求是,发现浏览器端数据为空,但是 curl 有数据,经排查,node 端也是有数据的,最终发现是因为请求使用了 gzip 压缩,而转发请求的时候,没有进行响应头处理,导致浏览器端收到已经解压的数据后,仍然以为数据未解压。
解决 移除 content-encoding 响应头和content-length 响应头
解释 关于 content-encoding 的工作原理:
HTTP 压缩机制: 当服务器发送响应时,为了减少传输数据大小,使用了 gzip 压缩 这体现在响应头:content-encoding: gzip 这表示响应体是经过 gzip 算法压缩的
双重解压的问题: 在我们的代理服务器中,fetch API 已经自动处理了 gzip 解压缩 但是我们保留了原始的 content-encoding: gzip 头 当这个响应转发到浏览器时,浏览器看到 content-encoding: gzip,会尝试再次解压缩 但数据已经被 fetch 解压过了,所以浏览器解压时会失败或得到错误的数据
解决方案:
const responseHeaders = new Headers(response.headers)
// 移除 content-encoding 头,因为我们会自动处理解压缩
responseHeaders.delete('content-encoding')
通过删除 content-encoding 头,我们告诉浏览器这是未压缩的数据 这样浏览器就会直接使用数据,而不会尝试解压缩
- 数据流程:
原始流程:
服务器(压缩数据 + gzip头) -> 代理(自动解压) -> 浏览器(尝试解压已解压的数据) ❌
修改后流程:
服务器(压缩数据 + gzip头) -> 代理(自动解压 + 移除gzip头) -> 浏览器(直接使用) ✅
- 为什么 fetch 会自动解压:
Node.js 的 fetch API 默认会处理常见的内容编码(如 gzip、deflate) 这是为了简化开发者的使用体验 当它看到 content-encoding: gzip 时,会自动解压数据,但不会自动移除这个头
这就是为什么删除 content-encoding 头能解决问题 - 它避免了数据被重复解压缩的问题,确保了数据在整个传输链路中只被解压缩一次。