response.setHeader 用法与常见错误

response.setHeader 是服务端往 HTTP 响应里写头信息的方法。ASP 里是 Response.SetHeader,Java Servlet 里是 response.setHeader,Node.js 里是 res.setHeader。名字一样,用法有差别,踩的坑却是同一批。

一、基本用法

// ASP
Response.SetHeader "Content-Type", "text/html; charset=utf-8"

// Java Servlet
response.setHeader("Content-Type", "application/json;charset=UTF-8");
response.setHeader("Cache-Control", "no-cache");

// Node.js
res.setHeader('Content-Type', 'application/json');
res.setHeader('X-Request-Id', 'abc123');

二、setHeader 和 addHeader 的区别

setHeader 是覆盖:同名头已经存在时,把旧值替换掉。addHeader 是追加:同名头可以出现多次,值以逗号分隔(Java 里实际是新增一个同名字段)。

需要多个值的场景,比如 Set-Cookie,必须用 addHeader,用 setHeader 会让前面设置的 Cookie 丢失:

response.addHeader("Set-Cookie", "a=1; Path=/");
response.addHeader("Set-Cookie", "b=2; Path=/");

三、最常见的五个错误

1. 响应已经提交后再设置头

Java 里报 java.lang.IllegalStateException: Cannot set a header after the response has been committed。原因是在 out.write() 或 out.flush() 之后才调用 setHeader。解决办法是把所有 header 设置放在写响应体之前,或者用 buffer 加大缓冲区。

2. 中文乱码

Content-Type 只写 text/html 不写 charset,浏览器会按本地编码猜,中文必然乱。正确写法是 text/html; charset=utf-8,并且 setCharacterEncoding 要在这之前调用,否则不生效。

3. 重定向写错

response.setStatus(302);
response.setHeader("Location", "/login");

只写 Location 不改状态码,浏览器不会跳转,会当成普通响应处理。用 sendRedirect 更省事,它会自动处理状态码和编码。

4. 跨域头写重复

Access-Control-Allow-Origin 设置两次会导致浏览器报 "The 'Access-Control-Allow-Origin' header contains multiple values"。常见于网关和业务代码各加了一遍。要么只在网关加,要么业务代码里判断已存在就不加。

5. 下载文件名乱码

response.setHeader("Content-Disposition",
    "attachment; filename*=UTF-8''" + URLEncoder.encode(name, "UTF-8"));

老写法 filename=中文 在部分浏览器里会乱,用 RFC 5987 的 filename* 更稳。

四、几个实用头

  • Cache-Control:no-store 禁止缓存,max-age=3600 缓存一小时
  • X-Content-Type-Options: nosniff:禁止浏览器猜测内容类型,防 XSS
  • X-Frame-Options: DENY:禁止被 iframe 嵌套,防点击劫持
  • Strict-Transport-Security:强制 HTTPS
  • Content-Security-Policy:限制资源加载来源

五、调试时怎么看

浏览器 F12 → Network → 点请求 → Response Headers,能看到最终发出的所有头。如果代码里设了但这里看不到,说明被中间的 Nginx 或 CDN 改写了,或者是响应提交后才设置导致没生效。curl -I 看一遍原始响应也很直接。