content-encoding relate problem

问题
工作中,使用 nextJs 代理请求是,发现浏览器端数据为空,但是 curl 有数据,经排查,node 端也是有数据的,最终发现是因为请求使用了 gzip 压缩,而转发请求的时候,没有进行响应头处理,导致浏览器端收到已经解压的数据后,仍然以为数据未解压。

解决
移除 content-encoding 响应头和content-length 响应头

解释
关于 content-encoding 的工作原理:

  1. HTTP 压缩机制:
    当服务器发送响应时,为了减少传输数据大小,使用了 gzip 压缩
    这体现在响应头:content-encoding: gzip
    这表示响应体是经过 gzip 算法压缩的

  2. 双重解压的问题:
    在我们的代理服务器中,fetch API 已经自动处理了 gzip 解压缩
    但是我们保留了原始的 content-encoding: gzip 头
    当这个响应转发到浏览器时,浏览器看到 content-encoding: gzip,会尝试再次解压缩
    但数据已经被 fetch 解压过了,所以浏览器解压时会失败或得到错误的数据

  3. 解决方案:

    1
    2
    3
    const responseHeaders = new Headers(response.headers)
    // 移除 content-encoding 头,因为我们会自动处理解压缩
    responseHeaders.delete('content-encoding')

通过删除 content-encoding 头,我们告诉浏览器这是未压缩的数据
这样浏览器就会直接使用数据,而不会尝试解压缩

  1. 数据流程:

    1
    2
    3
    4
    5
    原始流程:
    服务器(压缩数据 + gzip头) -> 代理(自动解压) -> 浏览器(尝试解压已解压的数据) ❌

    修改后流程:
    服务器(压缩数据 + gzip头) -> 代理(自动解压 + 移除gzip头) -> 浏览器(直接使用) ✅
  2. 为什么 fetch 会自动解压:

    Node.js 的 fetch API 默认会处理常见的内容编码(如 gzip、deflate)
    这是为了简化开发者的使用体验
    当它看到 content-encoding: gzip 时,会自动解压数据,但不会自动移除这个头

这就是为什么删除 content-encoding 头能解决问题 - 它避免了数据被重复解压缩的问题,确保了数据在整个传输链路中只被解压缩一次。

作者

Han Wei

发布于

2024-12-09

更新于

2024-12-09

许可协议

评论