HTTP核心知识

20 分钟

面试为什么反复考 HTTP 基础

框架帮你封装了请求库,Axios 帮你处理了大部分 header,但面试官关心的不是你会不会用 fetch,而是你是否理解一个请求从组装到发出、从响应到解析的每一个环节。状态码选错会让客户端做出错误决策,Content-Type 配错会导致参数解析失败,缓存头不理解就只能靠"清缓存大法"排问题。

这篇文章围绕 HTTP 报文结构、状态码、请求方法、常用头字段、Content-Type 和 RESTful 设计规范展开,覆盖面试中最高频的追问点。

HTTP 报文结构

HTTP 通信的基本单位是报文。请求和响应各有一套固定结构,理解它们是读懂所有后续知识的前提。

请求报文

一个 HTTP 请求报文由四部分组成:

POST /api/users HTTP/1.1          ← 请求行:方法 + 路径 + 协议版本
Host: example.com                  ← 请求头(多行 key: value)
Content-Type: application/json
Authorization: Bearer eyJhbGci...

{"name": "nanyi", "role": "fe"}   ← 请求体(GET 请求通常没有)
  • 请求行:包含 HTTP 方法(GET、POST、PUT、DELETE 等)、请求路径(含查询参数)和协议版本。
  • 请求头:一系列键值对,用来描述客户端环境、期望的响应格式、认证信息、缓存策略等。
  • 空行:标志头部结束、正文开始。
  • 请求体:携带要发送给服务端的数据,GET 请求通常没有请求体。

响应报文

HTTP/1.1 200 OK                   ← 响应行:协议版本 + 状态码 + 状态描述
Content-Type: application/json
Set-Cookie: sid=abc123; HttpOnly
Cache-Control: no-cache

{"id": 1, "name": "nanyi"}        ← 响应体
  • 响应行(状态行):协议版本、三位数状态码和一段可读描述。
  • 响应头:描述响应的元信息,如内容类型、缓存策略、Cookie 设置、跨域授权等。
  • 空行 + 响应体:服务端返回的实际数据。

面试追问:请求头和请求体之间靠什么分隔? 答案是一个空行(\r\n\r\n),这也是 HTTP/1.x 文本协议解析的关键分界点。

状态码分类详解

状态码是服务端对请求的"一句话判决"。三位数字,第一位决定类别。

1xx — 信息性

表示请求已被接收,继续处理。实际开发中很少直接接触。

  • 100 Continue:客户端发送了带 Expect: 100-continue 的请求头,服务端告知"可以继续发送请求体"。常见于上传大文件时,客户端先试探服务端是否愿意接收。

2xx — 成功

状态码含义典型场景
200 OK请求成功,响应体包含结果GET 查询、POST 提交后返回结果
201 Created资源创建成功POST 创建用户、文章等
204 No Content成功但无响应体DELETE 删除资源、PUT 更新后无需返回内容

面试追问:POST 成功后应该返回 200 还是 201? 如果是创建型操作(新增用户、新建订单),语义上应返回 201,且响应头中可以带 Location 指向新资源的地址。如果只是触发一次计算或查询,200 更合适。

3xx — 重定向

状态码含义特点
301 Moved Permanently永久重定向浏览器会缓存,下次直接访问新地址;搜索引擎会更新索引
302 Found临时重定向浏览器不会缓存,每次仍请求原地址
304 Not Modified资源未修改,使用缓存配合 If-None-Match / If-Modified-Since 使用
307 Temporary Redirect临时重定向,但不允许改变请求方法POST 请求被 307 重定向后仍保持 POST

面试高频追问:

301 和 302 的区别? 核心在于浏览器是否缓存重定向。301 是永久的,浏览器记住后不再访问原 URL;302 是临时的,每次还是先访问原 URL。从 SEO 角度,301 会让搜索引擎把权重转移到新地址。

302 和 307 的区别? 302 在规范上允许浏览器把 POST 请求在重定向时降级为 GET(历史包袱,实际浏览器也确实这么干了),307 则严格禁止改变方法。如果你的 POST 请求被重定向后还需要保持 POST,必须用 307。

304 是怎么工作的? 浏览器第一次请求资源时,服务端返回 ETagLast-Modified。后续请求时浏览器带上 If-None-MatchIf-Modified-Since,服务端比对后发现资源没变,返回 304 且不附带响应体,浏览器直接用本地缓存。

4xx — 客户端错误

状态码含义典型场景
400 Bad Request请求格式错误或参数非法JSON 格式错误、必填参数缺失、枚举值不合法
401 Unauthorized未认证未携带 token 或 token 过期
403 Forbidden已认证但无权限普通用户访问管理后台接口
404 Not Found资源不存在URL 拼错、资源已被删除

面试追问:401 和 403 的区别? 401 是"你是谁我不知道"(身份未认证),403 是"我知道你是谁,但你没权限"(认证通过但授权失败)。很多后端错误地在未登录时返回 403,这在语义上是不准确的。

5xx — 服务端错误

状态码含义典型场景
500 Internal Server Error服务端未预期的错误代码抛异常、空指针、数据库查询失败
502 Bad Gateway网关收到了上游服务的无效响应Nginx 转发请求到 Node 服务,Node 进程崩溃
503 Service Unavailable服务暂时不可用服务正在部署、过载限流、依赖的下游服务挂了

面试追问:502 和 503 有什么区别? 502 意味着网关层(如 Nginx、API Gateway)确实尝试转发了请求,但上游返回了不合法的响应或连接失败。503 是服务自身告诉你"我现在忙不过来",可能在做滚动发布、触发了限流或健康检查失败被摘流量。

GET vs POST

这可能是面试里被问得最多的 HTTP 题目。表面上只是"参数放 URL 还是 Body",实际涉及语义、安全性、幂等性和缓存策略。

维度GETPOST
语义获取资源提交数据、创建资源、触发操作
参数位置URL 查询字符串(?key=value请求体
URL 长度限制浏览器有限制(Chrome 约 2MB URL,但实际 query string 建议 2KB 以内)无实质限制(受服务端配置约束)
幂等性幂等:多次请求结果一致非幂等:多次提交可能创建多条记录
缓存默认可缓存默认不可缓存
安全性参数暴露在 URL 中,可被浏览历史、日志、Referer 泄露参数在请求体中,不会出现在 URL 中
书签/分享可以被收藏为书签无法收藏
回退行为浏览器回退时不会重新提交浏览器回退时会提示重新提交

几个容易踩坑的细节:

POST 比 GET 更安全? 不完全对。POST 只是参数不在 URL 里,但请求体在 HTTP 明文传输中一样能被抓包。真正的安全要靠 HTTPS 加密。POST 的"安全"更多是指参数不会出现在浏览器历史、代理日志和 Referer 头中。

GET 请求可以有 Body 吗? HTTP 规范没有禁止 GET 带 Body,但大多数浏览器、网关和框架不支持或会忽略。ElasticSearch 曾经用 GET + Body 做查询,后来也加了 POST _search 的写法。工程实践中不建议这样做。

幂等性为什么重要? 网络不稳定时客户端可能重试。如果 GET 是幂等的,重试不会有副作用;如果 POST 不做幂等处理,重试可能导致重复下单、重复扣款。这是面试里从"GET vs POST"延伸到"接口幂等设计"的常见路径。

常用请求头

Content-Type

告诉服务端请求体的数据格式,直接决定服务端如何解析参数。

Content-Type: application/json
Content-Type: application/x-www-form-urlencoded
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary

这三个值是前端开发中最常用的,后面会单独展开。

Accept

告诉服务端客户端期望的响应格式。服务端可以据此做内容协商(Content Negotiation)。

Accept: application/json
Accept: text/html, application/xhtml+xml
Accept: */*

Authorization

携带认证信息,最常见的是 Bearer Token:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

也有 Basic Auth 的写法(Base64 编码用户名和密码),但现代应用基本都用 Token。

浏览器自动携带该域名下的 Cookie:

Cookie: sid=abc123; theme=dark; _ga=GA1.2.12345

每次请求都会带上,这也是为什么不建议在 Cookie 中存放大量数据——它会增大每个请求的体积。

Origin

标识请求的来源(协议 + 域名 + 端口),在跨域请求和 CORS 预检中起关键作用:

Origin: https://blog.example.com

服务端根据 Origin 决定是否允许跨域访问。注意 Origin 和 Referer 的区别:Origin 只有协议+域名+端口,不包含路径;Referer 包含完整 URL 路径。

Referer

标识当前请求是从哪个页面发起的:

Referer: https://blog.example.com/posts/httpBasics

常用于统计来源、防盗链。注意 Referer 是 HTTP 规范中的历史拼写错误(正确应为 Referrer),但约定俗成一直沿用。

User-Agent

描述客户端环境:

User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36

服务端可以据此做设备判断、浏览器兼容或返回不同的页面版本。

常用响应头

服务端通过该头设置 Cookie:

Set-Cookie: sid=abc123; Path=/; HttpOnly; Secure; SameSite=Lax; Max-Age=86400

关键属性:

  • HttpOnly:禁止 JavaScript 通过 document.cookie 读取,防 XSS 窃取。
  • Secure:仅在 HTTPS 下传输。
  • SameSite:控制跨站请求是否携带 Cookie,Lax 是现代浏览器的默认值。
  • Max-Age / Expires:控制过期时间,不设置则为会话级 Cookie(关闭浏览器即失效)。

Cache-Control

控制缓存策略,是浏览器缓存机制的核心头字段:

Cache-Control: public, max-age=31536000    // 静态资源,缓存一年
Cache-Control: no-cache                     // 每次使用前必须去服务端验证
Cache-Control: no-store                     // 完全禁止缓存

面试追问:no-cacheno-store 的区别? no-cache 并不是"不缓存",而是"缓存了,但每次使用前要去服务端验证是否过期"(配合 ETag / Last-Modified)。no-store 才是真正的"完全不缓存",任何中间代理和浏览器都不得保存响应。

Access-Control-* 系列(CORS)

跨域资源共享相关的响应头:

Access-Control-Allow-Origin: https://blog.example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 86400
  • Allow-Origin:允许哪些源访问。设为 * 时不能同时设置 Allow-Credentials: true
  • Allow-Methods:允许的 HTTP 方法。
  • Allow-Headers:允许的自定义请求头。
  • Allow-Credentials:是否允许跨域携带 Cookie。
  • Max-Age:预检请求(OPTIONS)的结果可以缓存多久,避免每次跨域请求都发两次。

Content-Type(响应)

告诉客户端响应体的数据格式:

Content-Type: application/json; charset=utf-8
Content-Type: text/html; charset=utf-8
Content-Type: application/octet-stream

Location

配合 3xx 重定向使用,指示客户端跳转到的新地址。也可在 201 响应中指向新创建资源的 URL:

HTTP/1.1 301 Moved Permanently
Location: https://new.example.com/posts/httpBasics

Content-Type 三大常见值

前端发请求时,Content-Type 选错是一个高频 bug 来源。

application/json

最常用的格式。请求体是 JSON 字符串:

POST /api/users HTTP/1.1
Content-Type: application/json

{"name": "nanyi", "role": "fe"}

后端通常通过 body-parser 或框架内置中间件解析。Axios 默认使用这个格式。

application/x-www-form-urlencoded

HTML <form> 表单的默认提交格式。参数以 key=value&key=value 的形式编码在请求体中:

POST /api/login HTTP/1.1
Content-Type: application/x-www-form-urlencoded

username=nanyi&password=123456

特殊字符会被 URL 编码(空格 → +%20)。jQuery 的 $.ajax 默认使用这个格式,Axios 需要手动用 qs.stringify()URLSearchParams 转换。

multipart/form-data

用于文件上传或混合数据提交。每个字段用 boundary 分隔:

POST /api/upload HTTP/1.1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA

------WebKitFormBoundary7MA
Content-Disposition: form-data; name="file"; filename="avatar.png"
Content-Type: image/png

(二进制数据)
------WebKitFormBoundary7MA
Content-Disposition: form-data; name="userId"

12345
------WebKitFormBoundary7MA--

使用 FormData API 构建请求时,浏览器会自动设置 Content-Type 和 boundary,不要手动设置 Content-Type,否则 boundary 对不上会导致服务端解析失败。

面试追问:为什么文件上传不用 application/json JSON 是文本格式,二进制文件需要 Base64 编码后才能放进 JSON 字符串里,体积会膨胀约 33%,且解码有额外开销。multipart/form-data 原生支持二进制流传输,更高效。

RESTful API 设计规范

REST(Representational State Transfer)不是协议,是一种 API 设计风格。核心思想是把后端数据抽象成"资源",用 URL 定位资源、用 HTTP 方法表达操作。

资源命名

✅ GET    /api/users           获取用户列表
✅ GET    /api/users/123       获取 ID 为 123 的用户
✅ POST   /api/users           创建用户
✅ PUT    /api/users/123       全量更新用户
✅ PATCH  /api/users/123       部分更新用户
✅ DELETE /api/users/123       删除用户

❌ GET    /api/getUser?id=123
❌ POST   /api/deleteUser
❌ POST   /api/updateUserName

核心原则:

  • URL 中用名词复数表示资源集合(/users 而非 /user/getUsers)。
  • 操作语义由 HTTP 方法承担,URL 中不要出现动词。
  • 层级关系用路径表示:/users/123/posts 表示用户 123 的文章列表。
  • 用查询参数处理过滤、分页和排序:/users?role=admin&page=2&limit=20

HTTP 方法语义

方法语义幂等安全典型响应
GET获取资源200
POST创建资源201 + Location
PUT全量替换资源200 或 204
PATCH部分更新资源200
DELETE删除资源204

这里的"安全"指的是该方法是否会修改服务端状态。GET 是安全的(只读),POST/PUT/DELETE 不安全(有副作用)。

"幂等"指多次调用同一接口的效果和一次调用一致。GET 天然幂等,DELETE 也是幂等的(删除一个已不存在的资源,结果还是"不存在"),PUT 是幂等的(全量替换多次结果一致),POST 不幂等(每次调用可能创建新资源)。

状态码使用

RESTful API 应该正确使用状态码,而不是所有接口一律返回 200 然后把真正的错误码塞到响应体里。

// ❌ 不推荐:永远 200,错误信息藏在 body 里
HTTP/1.1 200 OK
{"code": 40401, "message": "user not found"}

// ✅ 推荐:用 HTTP 状态码表达语义
HTTP/1.1 404 Not Found
{"error": "User not found", "detail": "No user with id 999"}

常见对应关系:

  • 查询成功 → 200
  • 创建成功 → 201
  • 更新 / 删除成功且不返回内容 → 204
  • 参数校验失败 → 400
  • 未认证 → 401
  • 无权限 → 403
  • 资源不存在 → 404
  • 服务端异常 → 500

总结

HTTP 基础知识在面试中是常驻考点,原因是它贯穿了前后端协作的每一个环节。把这些知识串起来:

  • 报文结构是理解一切的基础:请求行/头/体、响应行/头/体,空行是分界符。
  • 状态码是服务端的语义表达:2xx 成功、3xx 重定向、4xx 客户端错误、5xx 服务端错误。记住常用的十几个,能说清 301 vs 302、304 的协商缓存流程、401 vs 403 的区别。
  • GET vs POST 不只是参数位置的差异,背后是语义、幂等性、缓存和安全的综合考量。
  • 请求头和响应头是 HTTP 能力的载体:Content-Type 决定解析方式、Authorization 承载认证、Cache-Control 控制缓存、CORS 头处理跨域。
  • Content-Type 选错是接口联调中最常见的低级错误,三种格式各有适用场景。
  • RESTful 是用 HTTP 语义设计 API 的最佳实践:用 URL 表示资源、用方法表示操作、用状态码表示结果。

面试时不要只背结论。能结合自己项目中遇到的跨域问题、缓存坑或 Content-Type 排查经历来展开,比背八股更有说服力。