【网络基础】HTTP/HTTPS 原理图解

用图解的方式深入理解 HTTP 协议的核心概念、请求响应流程、状态码含义,以及 HTTPS 如何保证安全通信。

17 分钟阅读
小明

【网络基础】HTTP/HTTPS 原理图解

「为什么我的网站要加 HTTPS?」 「HTTP 1.1 和 HTTP 2 有什么区别?」 「304 状态码是什么意思?」

作为前端开发者,我们每天都在和 HTTP 打交道,但很多人只知道 fetchaxios 怎么用,却不知道底层发生了什么。

今天小明用大量图解,带你彻底搞懂 HTTP 和 HTTPS。


HTTP 是什么?

一句话定义

HTTP(HyperText Transfer Protocol)是超文本传输协议,用于在客户端和服务器之间传输数据。

你在浏览器输入网址,按下回车,浏览器就会通过 HTTP 协议向服务器「要东西」。服务器收到请求后,把「东西」(HTML、图片、JSON 等)发回来。

┌─────────────┐                      ┌─────────────┐
│   浏览器    │ ───── HTTP 请求 ────▶│   服务器    │
│  (客户端)   │                      │             │
│             │◀──── HTTP 响应 ─────│             │
└─────────────┘                      └─────────────┘

HTTP 的特点

  1. 基于 TCP:HTTP 建立在 TCP 连接之上,保证数据可靠传输
  2. 无状态:每个请求都是独立的,服务器不记得「你是谁」
  3. 请求-响应模式:客户端发请求,服务器返响应
  4. 灵活:可以传输任何类型的数据(HTML、JSON、图片、视频...)

HTTP 请求详解

请求的组成

一个 HTTP 请求由三部分组成:

┌────────────────────────────────────────┐
│           请求行 (Request Line)         │
│  GET /api/users HTTP/1.1               │
├────────────────────────────────────────┤
│           请求头 (Headers)              │
│  Host: api.example.com                 │
│  Content-Type: application/json        │
│  Authorization: Bearer xxx             │
├────────────────────────────────────────┤
│           请求体 (Body)                 │
│  {"name": "小明", "age": 18}           │
└────────────────────────────────────────┘

请求方法

方法用途幂等性请求体
GET获取资源✅ 是通常无
POST创建资源❌ 否
PUT更新资源(全量)✅ 是
PATCH更新资源(部分)❌ 否
DELETE删除资源✅ 是通常无
HEAD获取响应头(不要 body)✅ 是
OPTIONS获取支持的方法(预检请求)✅ 是

幂等性:多次请求的效果和一次请求相同。GET 获取 10 次数据,数据不会变;POST 提交 10 次,可能创建 10 条记录。

常见请求头

// 发起一个请求
fetch('https://api.example.com/users', {
  method: 'POST',
  headers: {
    // 告诉服务器请求体的格式
    'Content-Type': 'application/json',
    
    // 告诉服务器期望的响应格式
    'Accept': 'application/json',
    
    // 身份认证
    'Authorization': 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
    
    // 自定义请求头
    'X-Request-ID': '123456',
  },
  body: JSON.stringify({ name: '小明' }),
});
请求头作用
Host目标主机名
Content-Type请求体的格式
Accept期望的响应格式
Authorization身份认证信息
Cookie携带的 Cookie
User-Agent客户端信息
Referer请求来源页面
Cache-Control缓存控制

HTTP 响应详解

响应的组成

┌────────────────────────────────────────┐
│           状态行 (Status Line)          │
│  HTTP/1.1 200 OK                       │
├────────────────────────────────────────┤
│           响应头 (Headers)              │
│  Content-Type: application/json        │
│  Content-Length: 1234                  │
│  Set-Cookie: session=abc123            │
├────────────────────────────────────────┤
│           响应体 (Body)                 │
│  {"id": 1, "name": "小明"}             │
└────────────────────────────────────────┘

状态码:服务器在「说话」

状态码是服务器告诉客户端「这次请求怎么样了」。

状态码分类:
1xx - 信息性状态码(请求还在处理中)
2xx - 成功状态码(请求成功)
3xx - 重定向状态码(需要进一步操作)
4xx - 客户端错误(你的问题)
5xx - 服务器错误(我的问题)

常见状态码

2xx 成功

状态码含义场景
200OK,成功最常见的成功响应
201Created,已创建POST 创建资源成功
204No Content,无内容DELETE 成功,不返回内容

3xx 重定向

状态码含义场景
301永久重定向网址永久更换
302临时重定向临时跳转
304Not Modified缓存有效,直接用缓存

4xx 客户端错误

状态码含义场景
400Bad Request请求参数有问题
401Unauthorized未登录/Token 过期
403Forbidden没有权限
404Not Found资源不存在
405Method Not Allowed请求方法不支持
429Too Many Requests请求太频繁

5xx 服务器错误

状态码含义场景
500Internal Server Error服务器内部错误
502Bad Gateway网关/代理错误
503Service Unavailable服务暂不可用
504Gateway Timeout网关超时

小明冷笑话时间:

404 和 500 有什么区别? 404 是「你要的东西我没有」 500 是「你要的东西我有,但我找不到了」


HTTP 版本演进

HTTP/1.0:一次一个

客户端                          服务器
   │                              │
   │──── TCP 连接 ────────────────▶│
   │──── HTTP 请求 ───────────────▶│
   │◀─── HTTP 响应 ────────────────│
   │◀─── 关闭连接 ─────────────────│
   │                              │
   │──── TCP 连接(再来一次)───────▶│
   │──── HTTP 请求 ───────────────▶│
   │◀─── HTTP 响应 ────────────────│
   │◀─── 关闭连接 ─────────────────│

问题:每个请求都要新建 TCP 连接,开销很大。

HTTP/1.1:连接复用

客户端                          服务器
   │                              │
   │──── TCP 连接 ────────────────▶│
   │──── HTTP 请求 1 ─────────────▶│
   │◀─── HTTP 响应 1 ──────────────│
   │──── HTTP 请求 2 ─────────────▶│  同一个连接
   │◀─── HTTP 响应 2 ──────────────│  可以发多个请求
   │──── HTTP 请求 3 ─────────────▶│
   │◀─── HTTP 响应 3 ──────────────│
   │                              │

改进

  • Keep-Alive 连接复用
  • 支持管道化(pipelining)

问题:队头阻塞(Head-of-Line Blocking)——请求必须按顺序响应。

HTTP/2:多路复用

客户端                          服务器
   │                              │
   │──── TCP 连接 ────────────────▶│
   │                              │
   │  Stream 1: 请求 HTML         │
   │  Stream 2: 请求 CSS     ────▶│  同时发送
   │  Stream 3: 请求 JS           │  不用排队
   │                              │
   │◀──── Stream 2: 响应 CSS       │
   │◀──── Stream 3: 响应 JS        │  哪个好了
   │◀──── Stream 1: 响应 HTML      │  先返回哪个

改进

  • 多路复用:一个连接上并行发多个请求/响应
  • 头部压缩:减少重复传输的头部信息
  • 服务器推送:服务器可以主动推送资源
  • 二进制传输:更高效

HTTP/3:基于 QUIC

HTTP/3 不再使用 TCP,而是使用基于 UDP 的 QUIC 协议。

HTTP/1.1 & HTTP/2              HTTP/3
     │                           │
     TCP                        QUIC
     │                           │
     IP                          UDP
                                 │
                                 IP

改进

  • 解决 TCP 的队头阻塞问题
  • 连接建立更快(0-RTT)
  • 连接迁移(WiFi 切 4G 不断开)

HTTPS:加个锁

HTTP 的问题

HTTP 是明文传输的,这意味着:

你:密码是 123456
        │
        ▼
    ┌───────┐
    │ 中间人 │ 哈哈,我看到了
    └───────┘
        │
        ▼
服务器:收到密码 123456

任何在网络链路上的人(路由器、WiFi 热点、运营商)都能看到你传输的内容。

HTTPS = HTTP + TLS

HTTPS 在 HTTP 和 TCP 之间加了一层 TLS(Transport Layer Security) 加密。

HTTP                    HTTPS
  │                       │
  │                      TLS (加密)
  │                       │
 TCP                     TCP
  │                       │
  IP                      IP

TLS 握手过程(简化版)

客户端                                  服务器
   │                                      │
   │────── 1. Client Hello ───────────────▶│
   │       (支持的加密算法列表)              │
   │                                      │
   │◀───── 2. Server Hello ────────────────│
   │       (选择的加密算法 + 证书)           │
   │                                      │
   │────── 3. 验证证书 ────────────────────│
   │       生成对称密钥                     │
   │       用服务器公钥加密发送              │
   │                                      │
   │◀───── 4. 服务器用私钥解密 ─────────────│
   │       得到对称密钥                     │
   │                                      │
   │======= 使用对称密钥加密通信 ============│

核心点

  1. 非对称加密(公钥/私钥)安全地交换对称密钥
  2. 后续通信用对称加密(更快)

证书是什么?

证书是由**权威机构(CA)**颁发的「身份证」。

┌─────────────────────────────────────────┐
│              SSL/TLS 证书                │
├─────────────────────────────────────────┤
│  域名:www.example.com                   │
│  颁发机构:DigiCert                      │
│  有效期:2024-01-01 ~ 2025-01-01        │
│  公钥:MIIBIjANBgkqhkiG9w0BAQEFAA...    │
│  签名:由 CA 的私钥签名                   │
└─────────────────────────────────────────┘

浏览器内置了受信任的 CA 列表。当收到证书时,浏览器会:

  1. 检查证书是否由受信任的 CA 签发
  2. 检查证书是否过期
  3. 检查证书的域名是否匹配

如果验证失败,浏览器会显示警告⚠️。

为什么要用 HTTPS?

HTTPHTTPS
明文传输,可被窃听加密传输,无法窃听
可被篡改完整性校验,无法篡改
无法验证身份证书验证身份
不安全安全

现在 HTTPS 已经是标配了。Chrome 会把 HTTP 网站标记为「不安全」,Google 搜索也会优先收录 HTTPS 网站。


缓存机制

为什么需要缓存?

没有缓存:
用户 ──请求──▶ 服务器 ──响应──▶ 用户  (每次都要等)

有缓存:
用户 ──请求──▶ 本地缓存 ──命中──▶ 用户  (瞬间返回)

缓存可以:

  • 减少服务器压力
  • 减少网络传输
  • 提高加载速度

强缓存 vs 协商缓存

强缓存:直接用缓存,不问服务器

浏览器                              服务器
   │                                  │
   │  检查本地缓存                      │
   │  Cache-Control: max-age=3600     │
   │  (未过期)                        │
   │                                  │
   │  直接使用缓存,不发请求             │
   │  状态码:200 (from cache)         │

相关响应头:

  • Cache-Control: max-age=3600(缓存 3600 秒)
  • Expires: Wed, 21 Oct 2024 07:28:00 GMT(过期时间)

协商缓存:问服务器「缓存还能用吗?」

浏览器                              服务器
   │                                  │
   │  本地缓存过期了                    │
   │──── If-Modified-Since: xxx ──────▶│
   │     或 If-None-Match: xxx         │
   │                                  │
   │◀─── 304 Not Modified ─────────────│
   │     (缓存还能用)                  │
   │                                  │
   │  使用本地缓存                      │

相关头部:

  • Last-Modified / If-Modified-Since:基于修改时间
  • ETag / If-None-Match:基于内容哈希

缓存策略建议

// 静态资源(JS/CSS/图片):强缓存 + 文件名哈希
// Cache-Control: max-age=31536000 (一年)
// 文件名:bundle.abc123.js

// HTML 文件:协商缓存或不缓存
// Cache-Control: no-cache
// 每次都问服务器

// API 响应:通常不缓存或短时间缓存
// Cache-Control: no-store 或 max-age=60

CORS:跨域的那些事

什么是跨域?

浏览器的同源策略规定:只有协议、域名、端口都相同才算同源。

http://example.com/page.html 发起请求到:

✅ http://example.com/api      (同源)
❌ https://example.com/api     (协议不同)
❌ http://api.example.com/api  (域名不同)
❌ http://example.com:8080/api (端口不同)

不同源的请求会被浏览器拦截。

CORS 解决方案

服务器通过响应头告诉浏览器「允许跨域」:

// 服务器响应头
Access-Control-Allow-Origin: https://your-site.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Allow-Credentials: true

简单请求 vs 预检请求

简单请求:直接发送

满足条件:

  • 方法是 GET/HEAD/POST
  • 没有自定义请求头
  • Content-Type 是特定值

预检请求:先发 OPTIONS 请求

浏览器                              服务器
   │                                  │
   │──── OPTIONS /api/users ──────────▶│
   │     (预检请求)                    │
   │                                  │
   │◀─── 200 OK ───────────────────────│
   │     Access-Control-Allow-XXX      │
   │                                  │
   │──── POST /api/users ─────────────▶│
   │     (实际请求)                    │
   │                                  │

实战:分析一次完整的请求

打开浏览器开发者工具,访问一个网页,来看看实际发生了什么。

1. DNS 解析

浏览器先把域名转换成 IP 地址。

www.example.com → 93.184.216.34

2. TCP 连接

与服务器建立 TCP 连接(三次握手)。

3. TLS 握手(如果是 HTTPS)

建立安全连接,交换密钥。

4. 发送 HTTP 请求

GET /index.html HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0 ...
Accept: text/html
Accept-Encoding: gzip, deflate, br

5. 服务器处理

服务器收到请求,处理后返回响应。

6. 接收响应

HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 1234
Cache-Control: max-age=3600

<!DOCTYPE html>
<html>...

7. 渲染页面

浏览器解析 HTML,遇到 CSS/JS/图片再发起新请求。


总结

HTTP 核心知识点:

  1. 请求方法:GET 获取、POST 创建、PUT 更新、DELETE 删除
  2. 状态码:2xx 成功、3xx 重定向、4xx 客户端错误、5xx 服务器错误
  3. 版本演进:HTTP/1.1 连接复用 → HTTP/2 多路复用 → HTTP/3 基于 QUIC
  4. HTTPS:HTTP + TLS 加密,保证安全
  5. 缓存:强缓存直接用,协商缓存问一下
  6. CORS:跨域资源共享,需要服务器配合

记住:

HTTP 是前端和后端沟通的语言。学好 HTTP,调接口不再抓瞎。


小明冷笑话收尾:

问:HTTP 和 HTTPS 有什么区别? 答:差一个 S。 这个 S 代表 Secure(安全),也代表 Slow(稍微慢一点,因为要加密)。 但为了安全,这点性能损失完全值得。

问:为什么要有 OPTIONS 预检请求? 答:因为浏览器想问服务器:「我要发个复杂请求,你愿意接吗?」 这是浏览器的礼貌,也是安全的保障。

「理解 HTTP,就理解了 Web 的一半。」—— 小明