HTTP
HTTP 是浏览器面试的核心地带:报文、方法、状态码、头部、缓存、跨域、连接管理、HTTP/2/3、HTTPS 一网打尽。本文聚焦 HTTP 层,TCP/TLS 传输层细节见计算机网络笔记,缓存与跨域的完整展开见浏览器缓存与跨域。
HTTP 报文结构
HTTP 报文由哪几部分组成
- HTTP 报文分两类:请求报文与响应报文,结构都是三部分:start line(起始行)+ header(首部)+ body(主体)。
- 起始行与首部之间、首部与主体之间用空行(
\r\n\r\n)分隔;首部是键: 值的多行文本。
请求报文:
POST /api/login HTTP/1.1 ← 请求行:方法 + 路径 + 版本
Host: www.example.com
Content-Type: application/json
Content-Length: 32
{"username":"tom"} ← 请求体(可空)
响应报文:
HTTP/1.1 200 OK ← 状态行:版本 + 状态码 + 原因短语
Content-Type: application/json
Content-Length: 15
{"code": 0}请求报文:
POST /api/login HTTP/1.1 ← 请求行:方法 + 路径 + 版本
Host: www.example.com
Content-Type: application/json
Content-Length: 32
{"username":"tom"} ← 请求体(可空)
响应报文:
HTTP/1.1 200 OK ← 状态行:版本 + 状态码 + 原因短语
Content-Type: application/json
Content-Length: 15
{"code": 0}请求行与状态行分别怎么写
- 请求行:
方法 空格 请求目标 空格 HTTP版本。请求目标通常是路径 + 查询串(/path?a=1),也有绝对 URI(代理场景)与*(OPTIONS 通配)。 - 状态行:
HTTP版本 空格 状态码 空格 原因短语。原因短语是给人看的,机器只认状态码。 - 面试常考:状态行第一个数字表示响应类别(1xx~5xx),如
HTTP/1.1 404 Not Found。
Header 与 Body 的注意点
- Header 每行一个字段,字段名大小写不敏感;同一字段可多行出现(如多个
Set-Cookie)。 - 请求体与响应体都可能为空:GET 一般无 body;204 响应不允许带 body。
- Header 体积有软限制(服务器/网关通常限 8KB~64KB),超限会 431;Body 大小由
Content-Length或分块传输给出。 - 常见坑:前端用
XMLHttpRequest/fetch自定义头会触发 CORS 预检(见跨域节);Content-Length与实际 body 不符会被服务端判为错误请求。
HTTP 请求方法
GET 和 POST 有什么区别
- 语义:GET 表示「获取资源」(安全、只读);POST 表示「向服务器提交数据、创建/触发处理」。
- 幂等:GET 幂等(重复请求结果一致);POST 不保证幂等(重复提交可能创建多条订单)。
- 位置:GET 参数一般放 URL query,会被浏览器历史、日志、Referer 泄漏;POST 参数放 body,相对更隐蔽但不是加密(HTTPS 才加密)。
- 缓存:GET 默认可被缓存(强缓存/协商缓存);POST 一般不可缓存(除非显式带 freshness 信息)。
- 长度:URL 长度受浏览器/服务器限制(通常 2KB~8KB);POST body 容量大得多。
- 面试加分:语义化角度 "GET 用于查询,POST 用于变更",而不是"POST 更安全"。
什么是幂等,哪些方法幂等
- 幂等(idempotent):同一请求执行一次与执行多次,对服务器资源产生的效果相同(读到的响应可不同,如第二次 404)。
- 幂等方法:
GET、PUT、DELETE、HEAD、OPTIONS、TRACE。 - 非幂等:
POST、PATCH(按定义 PATCH 不保证幂等,但实现上可做到)。 - 追问点:DELETE 第一次返回 200、第二次返回 404,为什么仍幂等?——因为资源最终状态一致(都是不存在)。
PUT 和 POST、PATCH 怎么区分
| 方法 | 语义 | 幂等 | 典型场景 |
|---|---|---|---|
| POST | 提交/创建,交给服务器决定位置 | 否 | 新建订单、登录、上传 |
| PUT | 用请求体整体替换目标资源,URL 由客户端指定 | 是 | 更新 id=1 的用户(全量) |
| PATCH | 对资源做部分修改 | 不保证 | 只改用户昵称字段 |
- 面试口径:PUT 是"把资源放成这个样子"(全量、幂等);PATCH 是"对资源做增量修补";POST 是"提交处理"。
HEAD 和 OPTIONS 的用途
- HEAD:与 GET 相同但响应不带 body,用于只取元信息(检查资源是否存在、看 Content-Length、探活)。
- OPTIONS:询问服务器支持的能力(响应
Allow: GET, POST...);同时也是 CORS 预检请求使用的方法(见跨域节)。 - 应用:下载前先 HEAD 拿到文件大小做断点续传校验;接口文档自动生成常发 OPTIONS 探测。
什么是 RESTful 风格
- RESTful 是用 HTTP 语义组织接口的架构风格:资源用名词 URL 表达,操作用 HTTP 方法表达。
- 口诀:URL 里只有名词、没有动词;动词交给方法。
| 操作 | 非 REST(反例) | RESTful(正例) |
|---|---|---|
| 查用户 | GET /getUser?id=1 | GET /users/1 |
| 新建用户 | POST /createUser | POST /users |
| 改用户 | POST /updateUser | PUT /users/1(全量)/ PATCH /users/1(部分) |
| 删用户 | GET /deleteUser?id=1 | DELETE /users/1 |
- 补充要点:状态码表达结果(201 创建成功、204 删除成功、409 冲突);用查询参数过滤排序(
GET /users?role=admin&page=2);版本化/v1/users。
HTTP 状态码
状态码分几类
| 类别 | 含义 | 代表 |
|---|---|---|
| 1xx | 信息性响应 | 100 Continue、101 Switching Protocols |
| 2xx | 成功 | 200、201、204、206 |
| 3xx | 重定向 | 301、302、304、307、308 |
| 4xx | 客户端错误 | 400、401、403、404、405、429 |
| 5xx | 服务端错误 | 500、502、503、504 |
2xx 常见状态码
- 200 OK:请求成功,body 为结果。
- 201 Created:创建成功,Location 头指向新资源(POST/PUT 后常见)。
- 202 Accepted:已受理但异步处理中(消息队列、异步任务常用)。
- 204 No Content:成功但无 body(DELETE 成功、PUT 局部更新)。
- 206 Partial Content:范围请求成功(断点续传/视频拖动,配合 Range,见串讲 Q7)。
3xx 重定向:301/302/307/308 的区别
- 301 Moved Permanently:永久重定向。方法默认从 POST 改成 GET(历史上 GET 化),新地址用 Location 头给出,搜索引擎会更新收录。
- 302 Found:临时重定向。默认同样 GET 化;语义上"这次先去别处,下次还来问我"。
- 303 See Other:明确"请用 GET 去 Location 取结果"(POST 提交后跳结果页的标准做法)。
- 307 Temporary Redirect:临时重定向,保持原方法与原 body(POST 还是 POST)。
- 308 Permanent Redirect:永久重定向,保持方法(307 的永久版)。
- 面试常问:301 和 302 默认会把 POST 变 GET(老规范浏览器行为);要保证方法不变必须用 307/308;https→http 跳转、域名换新用 301。
304 是什么、怎么产生
- 304 Not Modified:协商缓存命中的响应——客户端带了
If-None-Match/If-Modified-Since,服务器判断资源没变,返回 304 且不带 body,浏览器用本地缓存。 - 与重定向不同:304 不跳转,只是"告诉浏览器继续用缓存"。
- 完整机制(Last-Modified/ETag 对比、优先级)见浏览器缓存的协商缓存一节。
4xx 客户端错误
- 400 Bad Request:请求语法/参数不对(缺字段、JSON 解析失败)。
- 401 Unauthorized:未认证/凭证失效,响应带
WWW-Authenticate提示如何认证;与 403 的区别是"你还没证明你是谁"。 - 403 Forbidden:已认证但无权限(或服务器拒绝透露存在性);与 404 常被安全设计故意互换。
- 404 Not Found:资源不存在(或不想暴露存在)。
- 405 Method Not Allowed:方法不支持(如只允许 GET 的接口收到 POST),响应应带
Allow头。 - 429 Too Many Requests:触发限流,配合
Retry-After头告知何时重试。 - 其他备查:408 请求超时、409 冲突(资源状态冲突)、413 请求体过大、415 媒体类型不支持、431 头过大。
5xx 服务端错误:502 与 504 怎么区分
- 500 Internal Server Error:服务器内部异常(代码抛错、没接住)。
- 501 Not Implemented:方法未实现。
- 502 Bad Gateway:网关/代理收到上游的无效响应(上游挂了、连不上、返回乱数据)。
- 503 Service Unavailable:服务器过载/维护中,配合
Retry-After。 - 504 Gateway Timeout:网关等上游响应超时(代理在规定时间内没等到上游回包)。
- 记忆:502 = 上游"回的东西不对/没连上";504 = 上游"回得太慢超时";503 = 我自己的服务暂时不可用。
HTTP 请求头与响应头
Content-Type 与 Accept(内容协商)
- Accept(请求头):客户端能接受的媒体类型与优先级,如
Accept: text/html,application/json;q=0.9,*/*;q=0.8。 - Content-Type(实体头,请求/响应都有):body 的实际类型,如
application/json、text/html; charset=utf-8、application/x-www-form-urlencoded、multipart/form-data; boundary=...(文件上传)。 - 配套:
Accept-Encoding: gzip, br→ 服务器压缩后响应带Content-Encoding: gzip;Accept-Language→Content-Language。 - 前端坑:POST JSON 忘记设
Content-Type: application/json,body 会被按表单解析;form-data与urlencoded结构不同,后端取参方式也不同。
Authorization 认证头
- 请求头
Authorization: Bearer <token>(JWT 常见)或Authorization: Basic base64(user:pass)。 - 服务器对未认证请求返回 401 +
WWW-Authenticate,提示认证方式。 - 与 Cookie 的区别:Cookie 由浏览器自动携带,Authorization 由业务代码显式携带(适合前后端分离、跨域)。
Cookie 与 Set-Cookie 头
- 响应头
Set-Cookie: name=value; Path=/; HttpOnly; SameSite=Lax; Max-Age=3600由服务器下发,浏览器存储后,后续同域请求自动带上请求头Cookie: name=value。 - 请求头
Cookie只是键值对串,不含属性;属性只在 Set-Cookie 时声明。 - 安全属性(HttpOnly/Secure/SameSite)的作用与细节见浏览器缓存与前端安全。
CORS 相关头有哪些
| 头 | 方向 | 作用 |
|---|---|---|
| Origin | 请求 | 声明来源(协议+域名+端口) |
| Access-Control-Allow-Origin | 响应 | 允许的源(* 或具体源) |
| Access-Control-Allow-Methods | 响应 | 允许的方法(预检应答) |
| Access-Control-Allow-Headers | 响应 | 允许的自定义头(预检应答) |
| Access-Control-Expose-Headers | 响应 | 允许 JS 读取的响应头白名单 |
| Access-Control-Allow-Credentials | 响应 | 是否允许携带凭证(Cookie) |
| Access-Control-Max-Age | 响应 | 预检结果可缓存秒数 |
Cache-Control 指令速查
- 响应指令:
public/private(谁能缓存)、max-age=秒(强缓存时长)、no-cache(可用但每次要回源校验=协商)、no-store(禁止一切缓存)、s-maxage(CDN/共享缓存专用)、must-revalidate(过期必须回源)。 - 请求指令:
no-cache(强制回源校验)、no-store、max-age=0(立即过期重新请求)、only-if-cached。 - 与
Expires(HTTP/1.0,绝对时间,客户端时间不可信)的优先级:Cache-Control优先。 - 详细机制与刷新行为(F5/Ctrl+F5)见浏览器缓存。
其他高频头一览
Host:目标主机+端口,HTTP/1.1 起必填(一台服务器挂多个域名靠它区分虚拟主机)。User-Agent:客户端标识(浏览器/系统版本)。Referer:来源页面(防盗链、统计);Origin是 CORS 用的"来源"(不含路径)。Connection:keep-alive/close(HTTP/1.1 默认 keep-alive)。Upgrade+ 101:协议升级(WebSocket 握手用)。X-Forwarded-For/X-Real-IP:代理转发后携带的真实客户端 IP(需信任代理才可信)。Range/Content-Range:范围请求(断点续传,见串讲 Q7)。Retry-After:429/503 时告知何时可重试。
Cookie 与 Session
为什么需要 Cookie(HTTP 无状态问题)
- HTTP 是无状态协议:服务器不记得上一个请求是谁发的,每个请求相互独立。
- 电商购物车、登录态都需要"认出你",于是用 Cookie 在客户端保存身份标识,每次请求自动捎带,让服务器"有状态"。
Cookie 的关键属性与安全
| 属性 | 作用 |
|---|---|
Domain / Path | 限定 cookie 在哪些域/路径下携带 |
Expires / Max-Age | 过期时间(Max-Age 优先级更高) |
HttpOnly | JS 无法读取,防 XSS 窃取 |
Secure | 仅 HTTPS 下传输 |
SameSite=Strict/Lax/None | 限制跨站携带,防 CSRF(Lax 是浏览器默认) |
- Cookie 单条上限约 4KB、总量有限;会随每次请求自动带上,增大流量,不适合存大对象(见浏览器缓存的存储对比)。
Session 机制与 SessionID
- Session 是服务端的状态:用户登录后,服务器建一条 session 记录并生成唯一
SessionID。 - SessionID 通过
Set-Cookie(常见名JSESSIONID)下发给浏览器,之后每次请求带上,服务器凭它找到对应 session。 - 集群部署问题:session 默认存在单机内存,多机需要 session 共享(粘性会话、Redis 集中存储、或换无状态 Token)。
Cookie 与 Session 的对比
| 维度 | Cookie | Session |
|---|---|---|
| 存储位置 | 客户端 | 服务端 |
| 容量 | 约 4KB | 取决于服务端实现(Redis/内存) |
| 安全性 | 明文存客户端,可被篡改/窃取 | 相对安全,但 SessionID 被偷同样危险 |
| 性能 | 请求自动携带,占带宽 | 每次查 session 有服务端开销 |
| 典型 | 记住偏好、跟踪标识 | 登录态、购物车 |
- 引申:如果只放"用户 ID"这类明文,不要在 Cookie 里直接信,要防篡改(签名/只存 SessionID)。
Token / JWT 与 Session 的区别
- JWT 是无状态的:签名信息(含过期时间、用户标识)放在 token 里,服务器验签即可,不查库/不存 session,天然适合分布式与跨域。
- 缺点:签发后难以主动失效(黑名单/短有效期+refresh token 补救);token 体积大。
- 面试结论:单体传统项目用 Session+Cookie 简单可靠;前后端分离/微服务/移动端多用 Token(JWT)。
缓存机制
强缓存与协商缓存一句话区分
- 强缓存:命中直接用本地缓存,不发请求(
200 from memory/disk cache)。 - 协商缓存:发请求问服务器,服务器说没变 → 304,浏览器用本地缓存;变了 → 200 + 新资源。
- 两者可共存,强缓存优先;强缓存没命中/过期才走协商。
- 完整原理(Expires/Cache-Control/Last-Modified/ETag、刷新操作影响)见浏览器缓存,本文不重复展开。
缓存相关头清单
| 类别 | 请求头 | 响应头 |
|---|---|---|
| 强缓存 | — | Expires(1.0)、Cache-Control: max-age/s-maxage(1.1) |
| 协商-时间 | If-Modified-Since | Last-Modified |
| 协商-标识 | If-None-Match | ETag |
| 条件-改后取 | If-Match | — |
| 禁止缓存 | Cache-Control: no-store(请求/响应均可) | — |
- 面试高频:ETag 比 Last-Modified 更准(内容变化才变、可解决秒级精度与内容没变但 mtime 变的问题),优先用 ETag。
跨域 CORS
什么是同源策略与跨域
- 同源 = 协议 + 域名 + 端口 三者一致;任一不同即为跨域,浏览器默认拦截跨域请求的响应读取(请求其实可能发出去了)。
- 跨域是浏览器的安全机制,不是服务器的限制;CORS 是服务端"开白名单"放行的标准方案。
- 完整方案清单(JSONP/CORS/代理)见跨域。
简单请求与预检请求
- 简单请求:方法为 GET/HEAD/POST 且仅用 CORS 安全头(Accept、Content-Type 为表单三兄弟等)→ 直接发,响应带
Access-Control-Allow-Origin即放行。 - 预检请求(Preflight):方法非简单(PUT/DELETE)、或带自定义头(如
Authorization)、或 Content-Type 为application/json→ 浏览器先发OPTIONS预检,服务器回Access-Control-Allow-*通过后才发真实请求。 - 面试题"为什么会有两次请求":就是预检;用
Access-Control-Max-Age可缓存预检结果减少次数。
跨域要带 Cookie 怎么办
- 前端:
fetch设credentials: 'include'(XHR 设withCredentials = true)。 - 后端:
Access-Control-Allow-Origin不能是*,必须是具体源,且响应头Access-Control-Allow-Credentials: true。 - Cookie 的
SameSite=None; Secure才能跨站发送,否则会被浏览器拦(Chrome 默认 Lax)。
连接管理:短连接、keep-alive 与队头阻塞
HTTP/1.0 与 HTTP/1.1 的差异
- HTTP/1.0:每次请求新建 TCP 连接、用完即关(短连接),且请求里无 Host 头(一台机器只能服务一个站点)。
- HTTP/1.1:默认 keep-alive 长连接(复用 TCP)、必须带
Host头(虚拟主机)、支持Range断点续传、管线化(pipelining)等。 - HTTP/1.1 默认长连接,
Connection: close可显式关闭;1.0 要长连则发Connection: keep-alive。
长连接是怎么实现的,有什么好处
- 长连接让多个 HTTP 请求复用同一条 TCP 连接,省去反复三次握手/慢启动的开销 → 减少延迟、降低服务器资源消耗。
- 浏览器对同域并发 TCP 连接数有限制(HTTP/1.1 一般 6 条),多出来的请求排队。
- 服务端通过超时与最大请求数回收空闲长连接(如 Nginx keepalive_timeout)。
什么是队头阻塞(HOL blocking)
- HTTP/1.1 队头阻塞:同一连接上前一个请求的响应没返回,后面的响应就得等(管线化在浏览器基本没启用)。
- 缓解手段:浏览器开多条 TCP 连接(并行 6 条)、域名分片(把静态资源放多个子域)。
- 注意:HTTP/1.1 的队头阻塞来自"请求-响应的串行",HTTP/2 解决了它但引入了新的 TCP 层队头阻塞(见下节)。
短连接与长连接怎么选
- 短连接:请求极少、低频小请求(省连接管理);缺点是每次握手开销大。
- 长连接:高并发高频请求(同域页面几十个静态资源、API 调用);缺点是占用连接资源、需处理超时/断线重连。
- 名词辨析:HTTP 长连接(keep-alive)是复用连接;TCP 长连接多指心跳保活(WebSocket 等场景)。
HTTP/2
HTTP/2 解决了什么问题
- 目标:解决 HTTP/1.1 的队头阻塞与头部冗余两大痛点,同时保持语义兼容(方法/状态码/头字段不变)。
- 核心手段:二进制分帧 + 多路复用 + 头部压缩 + 服务端推送。
- 上线前提:HTTPS(主流浏览器要求 HTTP/2 over TLS,ALPN 协商)。
二进制分帧与多路复用
- HTTP/1.x 报文是纯文本、按顺序解析;HTTP/2 把报文拆成一个个 frame(帧),不同请求的帧交错在同一 TCP 连接上传输,接收方按 stream id 重组。
- 一个连接上可同时跑多个 stream(流),互不等待 → 真正并行,消除了应用层的队头阻塞。
HTTP/1.1:连接串行 [请求A...响应A][请求B...响应B]
HTTP/2 :一个 TCP 连接里 A、B、C 的帧交错传输
── A1 B1 A2 C1 B2 A3 C2 ── (stream id 区分归属)HTTP/1.1:连接串行 [请求A...响应A][请求B...响应B]
HTTP/2 :一个 TCP 连接里 A、B、C 的帧交错传输
── A1 B1 A2 C1 B2 A3 C2 ── (stream id 区分归属)- 帧有优先级/依赖设置,可让首屏资源优先到达。
HPACK 头部压缩
- 请求头重复度高(Cookie、User-Agent 每次都一样)。HPACK 用静态表 + 动态表 + Huffman 编码把重复头压缩。
- 静态表:61 个常见头(如
:method: GET)用 1 字节索引代替。 - 动态表:连接内双方各自维护,出现过的头加入表,后续用索引引用。
- 效果:头部体积通常减少 80% 以上,尤其对小请求收益巨大。
Server Push(服务端推送)
- 服务器预测客户端会请求的资源(如 HTML 里的 CSS/JS),在收到 HTML 请求后主动推送,省去客户端再次请求的往返。
- 现实情况:需要服务端明确配置;浏览器支持度反复(Chrome 已默认关闭/移除),实践中优先级远低于"预加载"与 HTTP/3,面试了解即可。
HTTP/2 为什么还有队头阻塞
- HTTP/2 消除了HTTP 层的队头阻塞,但 HTTP/2 依赖 TCP:TCP 保证有序传输,一个包丢失会触发重传并阻塞后续所有数据,即使它们属于不同 stream。
- 这就是 TCP 层队头阻塞——丢包率高的弱网下,多路复用优势被抵消。
- 根治方案是 HTTP/3 把传输层换成基于 UDP 的 QUIC(每个 stream 独立可靠传输)。
HTTP/3(QUIC)
QUIC 为什么基于 UDP 还可靠
- HTTP/3 的传输层是 QUIC,跑在 UDP 之上,把"可靠传输、拥塞控制、加密、多路复用"全部在用户态实现。
- QUIC 内部给每个 stream 独立做可靠性与流量控制:一个 stream 丢包只重传该 stream,不影响其他 stream → 彻底消除 TCP 层队头阻塞。
- 连接建立更快:QUIC 握手合并了传输握手与 TLS 1.3 握手。
0-RTT 与连接迁移
- 0-RTT:与服务器通信过的客户端,重连时可携带上次协商的密钥直接发数据,省掉整个握手往返(有重放风险,用于安全要求不高的场景)。
- 1-RTT:首次连接需要一次往返完成握手(QUIC + TLS 1.3)。
- 连接迁移:连接用 Connection ID 标识而非四元组(IP+端口),手机切 WiFi/4G 时 IP 变了连接不断,不需要重连。
- 面试口径:HTTP/3 = HTTP 语义 + QUIC(UDP + TLS1.3 + 多路复用 + 0-RTT + 连接迁移)。
HTTP/3 的现状与问题
- 优势场景:弱网、移动网络、高丢包环境改善明显。
- 现实问题:UDP 在部分企业网络/中间设备被限速或封锁;服务端与 CDN 支持仍在普及;CPU 开销高于 TCP。
- 演进结论话术:HTTP/1.1(文本、串行)→ HTTP/2(二进制、多路复用,仍受 TCP 队头阻塞)→ HTTP/3(UDP+QUIC,解决传输层队头阻塞)。
HTTPS(HTTP 层视角)
HTTP 与 HTTPS 的区别
- HTTPS = HTTP + TLS/SSL:在 HTTP 与 TCP 之间加了一层 TLS,提供加密、完整性校验、身份认证。
- 默认端口:HTTP 80、HTTPS 443。
- 防窃听(明文变密文)、防篡改(MAC 校验)、防冒充(证书验身份);TLS 记录层的细节在计算机网络笔记。
TLS 握手流程(简要版)
- 一次典型握手(TLS 1.2,RSA/ECDHE 类):
- ClientHello:客户端发随机数、支持的加密套件列表。
- ServerHello:服务器选定套件、回自己的随机数;下发证书链。
- 客户端验证证书(链到受信根 CA),用证书公钥完成密钥交换(如 ECDHE 参数或加密预主密钥)。
- 双方各自算出会话密钥(对称密钥),互相发 Finished 确认。
- 之后应用数据用会话密钥对称加密传输。
- 简化记忆:非对称/证书完成"身份认证+密钥协商",对称加密承载"实际数据"。
- TLS 1.3:握手缩到 1-RTT,去掉弱套件,支持 0-RTT 恢复。
对称加密 + 非对称加密怎么配合
- 对称加密(AES 等):快,但密钥怎么安全地给到对方是难题。
- 非对称加密(RSA/ECDHE):公钥加密私钥解密,可安全交换,但慢、密文大。
- 配合:用非对称/密钥交换算法协商出一个临时会话密钥,之后全程用对称加密传输 → 兼顾安全与性能(混合加密)。
证书链与 CA 的作用
- 服务器证书由 CA(证书颁发机构) 签发;证书里含域名、公钥、有效期、CA 签名。
- 证书链:
服务器证书 ← 中间 CA 证书 ← 根 CA 证书,浏览器内置受信根证书库,逐级验证签名直到信任的根。 - 校验点:域名匹配(SAN)、有效期、链完整、是否被吊销(OCSP)。
- 自签名证书不会被浏览器信任,会报警告——这就是本地 https 调试常见的坑。
中间人攻击与防范
- 中间人(MITM):攻击者插在客户端与服务器之间,各自伪装成对方,解密/篡改流量。
- HTTPS 防 MITM 的关键是证书验证:客户端校验证书不是真的(自签名/域名不符/CA 不受信)时就应拒绝连接。
- 防范:不点"继续访问"绕过警告;企业/抓包工具装的根证书本质上就是"被信任的中间人";服务端开启 HSTS 强制 HTTPS、证书固定(pinning)用于高安全场景。
- 与前端安全的关系:XSS/CSRF 是应用层攻击,HTTPS 管的是传输链路安全,两者互补。
高频面试 Q&A 串讲(15 题)
Q1:输入 URL 到页面展示,HTTP 部分发生了什么
- 解析 URL → DNS 解析域名拿 IP → 建立 TCP 连接(三次握手)→ HTTPS 则先 TLS 握手 → 发 HTTP 请求(请求行+头+body)→ 服务器返回响应(状态行+头+body)→ 浏览器解析 HTML,期间并发请求 CSS/JS/图片(走缓存或网络)→ 页面渲染。
- 浏览器里看到的往往是一串请求而不是一个:主文档 + 静态资源各自是一次 HTTP 事务。
- 关联阅读:页面加载执行(渲染侧)、计算机网络笔记(TCP/DNS 细节)。
Q2:GET 与 POST 的完整区别
- 语义:GET 取资源、POST 提交变更;GET 幂等可缓存、POST 不保证。
- 载体:GET 参数在 URL(有长度限制、会进日志/历史记录),POST 参数在 body(容量大)。
- 安全:两者明文等价,都不安全;敏感数据必须靠 HTTPS,而不是靠 POST。
- 缓存与回退:GET 可被浏览器缓存、可刷新重放;POST 默认不缓存,重复提交要防重。
- 详见上文"HTTP 请求方法"一节。
Q3:304 是怎么产生的,与强缓存什么关系
- 流程:资源上次响应带
ETag/Last-Modified且缓存策略允许 → 本次请求带If-None-Match/If-Modified-Since→ 服务器比对未变化 → 返回 304 + 空 body → 浏览器用本地缓存。 - 关系:强缓存命中(
Cache-Control: max-age内)根本不发请求;发请求还返回 304 说明强缓存已过期、走的是协商缓存。 - 刷 F5 与 Ctrl+F5 对两者的影响见浏览器缓存。
Q4:长连接与短连接的区别和应用
- 短连接:一来一回就关 TCP(HTTP/1.0 默认),简单但握手开销大。
- 长连接:复用连接处理多个请求(HTTP/1.1 默认 keep-alive),省握手、可做管线化。
- 应用:Web 页面资源密集用长连接;低频接口/需要及时释放资源的场景考虑短连接;服务端要设超时防止连接泄漏。
Q5:HTTP/1.1、HTTP/2、HTTP/3 演进对比
| 维度 | HTTP/1.1 | HTTP/2 | HTTP/3 |
|---|---|---|---|
| 传输 | 文本 | 二进制分帧 | 二进制(QUIC) |
| 底层 | TCP | TCP | UDP + QUIC |
| 连接复用 | 串行(多 TCP) | 多路复用单连接 | 多路复用单连接 |
| 队头阻塞 | 有(应用层) | 传输层仍有(TCP) | 基本消除 |
| 头部压缩 | 无 | HPACK | QPACK |
| 握手 | 多次往返 | TLS 一般 1-2 RTT | 0/1-RTT |
| 其他 | Host 头/Range | Server Push | 连接迁移 |
Q6:HTTP/2 多路复用为什么还有队头阻塞
- 多路复用解决的是"请求在应用层排队";但所有 stream 共享一条 TCP,TCP 必须按序交付。
- 一旦底层丢包,TCP 重传期间后续数据全部卡住 → 不同 stream 互相拖累。
- 一句话:队头阻塞从 HTTP 层转移到了 TCP 层,丢包率越高影响越大。
Q7:断点续传 / 分块上传怎么实现
- 下载续传:请求头
Range: bytes=1024-(从 1024 字节开始),服务器支持则返回 206 Partial Content +Content-Range: bytes 1024-2047/2048,不支持就回 200 全量。 - 条件续传:带
If-Range: <etag>,资源没变才给 206,变了给 200 重新下。 - 上传分块:客户端把文件切片逐个上传,服务端记录每片偏移,最后合并;或用 multipart 多段;断点续传思路相同(记录已传偏移)。
- 应用:下载工具断点续传、大文件秒传(先传 hash 判重)。
Q8:SSE 与 WebSocket 的区别
| 维度 | SSE(Server-Sent Events) | WebSocket |
|---|---|---|
| 方向 | 服务器 → 客户端单向 | 全双工 |
| 协议 | 普通 HTTP(text/event-stream) | 独立协议(HTTP Upgrade 101 握手后) |
| 自动重连 | 自带(EventSource 自动重连+断点续传 id) | 需自己实现 |
| 消息格式 | 文本 | 文本/二进制 |
| 适用 | 服务端推送:通知、股票、日志流 | 聊天、游戏、双向实时交互 |
- 一句话:只要"服务器单向下发"(通知、feed),优先 SSE——基于 HTTP 简单、能过代理、自动重连;需要双向实时通信才上 WebSocket。
Q9:为什么需要 Content-Type 与 Accept,什么时候会有两次请求
- Accept 声明"我能接受什么",Content-Type 声明"这个 body 是什么"——两者配合完成内容协商(格式、压缩、语言)。
- 两次请求 = CORS 预检:非简单请求(自定义头、JSON、PUT/DELETE)先发 OPTIONS,通过后再发真实请求;可用
Access-Control-Max-Age缓存预检。 - 详见上文 CORS 节与跨域。
Q10:HTTP 无状态,登录态怎么保持
- 方案一 Session+Cookie:登录后服务端记 session,SessionID 下到 Cookie,请求自动携带。
- 方案二 Token(JWT):登录后发签名 token,客户端存起来(localStorage/内存),请求头带
Authorization: Bearer xxx。 - 方案三(简单场景)签名 Cookie 直接存用户标识。
- 横向扩展:Session 需集中存储(Redis),Token 天然适合分布式;见上文 Cookie 与 Session 节。
Q11:代理转发后怎么拿真实 IP
- 请求经过 Nginx/CDN 等反向代理,服务端看到的源 IP 是代理的。
- 代理把真实 IP 追加到
X-Forwarded-For(每经过一跳追加一个)或X-Real-IP(Nginx 常见)。 - 注意:这些头客户端可伪造,只有可信代理设置/覆盖时才可信;取"最左的第一个不可信段"要结合代理链配置判断。
Q12:chunked 分块传输是干什么的
Transfer-Encoding: chunked:响应体长度未知时(如流式输出、动态生成大文件),边生成边按"长度+块数据"发送,最后发一个 0 长度块结束。- 与
Content-Length互斥:有 chunked 就不写 Content-Length。 - 应用:SSE、大文件流式下载、服务端渲染流式输出;面试答"动态内容、长度未知、流式传输"即命中。
Q13:OPTIONS 预检在哪些场景出现
- 方法不是 GET/HEAD/POST;或 POST 的 Content-Type 不是表单三类;或带了自定义请求头(Authorization、X-Token);或请求带非简单头的 cookie 凭证场景。
- 出现两次请求先别慌:预检 200 只是"放行",看真实请求是否成功。
- 优化:合并请求头(少自定义头)、服务端配
Access-Control-Max-Age缓存预检。
Q14:CDN 缓存与浏览器缓存怎么配合
- CDN 是"中间层缓存":用户请求先打到 CDN 边缘节点,命中则直接返回,未命中才回源站。
- 控制头分两层:
Cache-Control: max-age管浏览器/本地;s-maxage/public管 CDN/共享缓存;no-cache让 CDN 每次回源校验。 - 版本号/文件名 hash(指纹)更新时,浏览器与 CDN 缓存一起失效 → 前端构建的静态资源更新策略。
- 详见浏览器缓存的缓存场景与静态资源指纹。
Q15:HTTPS 全流程一句话串讲
- 客户端连服务器 → TCP 三次握手 → TLS 握手(校验证书链确认身份 + 协商会话密钥)→ 之后 HTTP 报文用会话密钥对称加密传输 → 结束用四次挥手断开。
- 安全三件套:加密(防窃听)、完整性(防篡改)、认证(防冒充)。
- 密钥交换与握手细节、证书校验、中间人的关系见上文 HTTPS 一节与计算机网络笔记。
Cao Note