跳到主要内容

HTTP/HTTPS 协议详解

问题

HTTP 和 HTTPS 协议的核心概念是什么?请求方法、状态码、HTTPS 加密原理是怎样的?

答案

HTTP(HyperText Transfer Protocol)是互联网上应用最广泛的应用层协议,用于客户端与服务器之间的通信。

HTTP 协议概览


HTTP 请求方法

常用方法

方法语义幂等安全请求体响应体
GET获取资源
POST创建资源
PUT替换资源
PATCH部分更新
DELETE删除资源
HEAD获取头信息
OPTIONS查询支持方法
幂等性与安全性
  • 幂等:多次请求结果相同(GET、PUT、DELETE)
  • 安全:不修改服务器状态(GET、HEAD、OPTIONS)

GET vs POST

// GET 请求 - 参数在 URL 中
fetch('/api/users?page=1&size=10')
.then(res => res.json());

// POST 请求 - 参数在请求体中
fetch('/api/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'John', email: 'john@example.com' })
});
特性GETPOST
参数位置URL 查询字符串请求体
参数长度受 URL 长度限制(约 2KB)无限制
缓存可被缓存默认不缓存
书签可收藏为书签不能
历史记录参数保留在历史参数不保留
安全性参数暴露在 URL相对安全

HTTP 状态码

状态码分类

分类范围含义
1xx100-199信息性响应
2xx200-299成功
3xx300-399重定向
4xx400-499客户端错误
5xx500-599服务器错误

常见状态码

// 2xx 成功
200 OK // 请求成功
201 Created // 资源创建成功
204 No Content // 成功但无返回内容

// 3xx 重定向
301 Moved Permanently // 永久重定向(SEO 权重转移)
302 Found // 临时重定向
304 Not Modified // 资源未修改(协商缓存)
307 Temporary Redirect // 临时重定向(保持方法)
308 Permanent Redirect // 永久重定向(保持方法)

// 4xx 客户端错误
400 Bad Request // 请求语法错误
401 Unauthorized // 未认证
403 Forbidden // 无权限
404 Not Found // 资源不存在
405 Method Not Allowed // 方法不允许
429 Too Many Requests // 请求过多(限流)

// 5xx 服务器错误
500 Internal Server Error // 服务器内部错误
502 Bad Gateway // 网关错误
503 Service Unavailable // 服务不可用
504 Gateway Timeout // 网关超时

301 vs 302 vs 307 vs 308

状态码类型请求方法场景
301永久可能变为 GET域名迁移、URL 永久变更
302临时可能变为 GET临时跳转(登录后跳转)
307临时保持原方法POST 请求临时重定向
308永久保持原方法POST 请求永久重定向

HTTP Headers

请求头

GET /api/users HTTP/1.1
Host: api.example.com
User-Agent: Mozilla/5.0 ...
Accept: application/json
Accept-Language: zh-CN,zh;q=0.9
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
Cookie: session_id=abc123
Cache-Control: no-cache
头字段说明
Host目标主机(HTTP/1.1 必需)
User-Agent客户端信息
Accept可接受的响应类型
Authorization认证信息
Cookie携带的 Cookie
Cache-Control缓存控制
Content-Type请求体类型

响应头

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 1234
Content-Encoding: gzip
Cache-Control: max-age=3600
ETag: "abc123"
Set-Cookie: session_id=xyz789; HttpOnly; Secure
Access-Control-Allow-Origin: *
头字段说明
Content-Type响应体类型
Content-Length响应体长度
Cache-Control缓存策略
ETag资源标识(协商缓存)
Set-Cookie设置 Cookie
Access-Control-*CORS 相关

HTTPS

HTTPS = HTTP + TLS

HTTPS 通过 TLS(Transport Layer Security)协议提供:

  1. 加密:数据传输加密,防止窃听
  2. 完整性:数据校验,防止篡改
  3. 身份认证:证书验证,防止冒充

TLS 握手过程

TLS 1.3 改进

特性TLS 1.2TLS 1.3
握手延迟2-RTT1-RTT
0-RTT 恢复
加密套件复杂精简
前向安全可选强制

证书与 CA

// 查看证书信息(Node.js)
import https from 'https';

const req = https.get('https://example.com', (res) => {
const cert = res.socket.getPeerCertificate();
console.log('Subject:', cert.subject);
console.log('Issuer:', cert.issuer);
console.log('Valid from:', cert.valid_from);
console.log('Valid to:', cert.valid_to);
});

HTTP 版本演进

版本年份特点
HTTP/0.91991仅支持 GET,无头部
HTTP/1.01996头部、状态码、多种方法
HTTP/1.11997持久连接、管道化、Host
HTTP/22015多路复用、头部压缩、服务器推送
HTTP/32022基于 QUIC,0-RTT

常见面试问题

Q1: HTTP 和 HTTPS 的区别?

答案

特性HTTPHTTPS
端口80443
安全性明文传输加密传输
证书不需要需要 CA 证书
性能略慢(TLS 握手)
SEO不利有优势

Q2: GET 和 POST 的区别?

答案

维度GETPOST
语义获取数据提交数据
参数URL 中请求体中
长度有限制无限制
缓存可缓存不缓存
幂等
安全是(不修改数据)
注意

"POST 比 GET 安全" 是误解。两者都是明文传输(HTTP),真正的安全需要 HTTPS。

Q3: 常见状态码及含义?

答案

// 成功
200 // OK - 请求成功
201 // Created - 创建成功
204 // No Content - 成功但无内容

// 重定向
301 // 永久重定向
302 // 临时重定向
304 // 未修改(使用缓存)

// 客户端错误
400 // Bad Request - 请求错误
401 // Unauthorized - 未认证
403 // Forbidden - 无权限
404 // Not Found - 未找到

// 服务器错误
500 // Internal Server Error - 服务器错误
502 // Bad Gateway - 网关错误
503 // Service Unavailable - 服务不可用

Q4: HTTPS 是如何保证安全的?

答案

HTTPS 通过 TLS 协议实现三重保护:

  1. 机密性:对称加密(AES)加密数据
  2. 完整性:MAC 校验防止篡改
  3. 身份认证:CA 证书验证服务器身份

Q5: 输入 URL 到页面展示经历了哪些过程?

答案

  1. DNS 解析:域名 → IP 地址
  2. TCP 连接:三次握手建立连接
  3. TLS 握手:(HTTPS)建立安全连接
  4. 发送请求:构造 HTTP 请求
  5. 服务器处理:处理请求,返回响应
  6. 接收响应:解析响应内容
  7. 渲染页面:构建 DOM、CSSOM、渲染树
  8. TCP 断开:四次挥手关闭连接

详见 从输入 URL 到页面展示

Q6: HTTP 请求方法中 PUT 和 PATCH 的区别?幂等性是什么?

答案

PUT vs PATCH

PUTPATCH 都用于更新资源,但语义不同:

维度PUTPATCH
语义整体替换资源部分更新资源
幂等性幂等非幂等
请求体完整的资源表示仅包含需要修改的字段
缺失字段会被置为默认值或删除不受影响
interface User {
id: number;
name: string;
email: string;
age: number;
}

// PUT - 整体替换(必须传完整对象)
// 如果不传 age,age 会被置空或删除
const putUpdate = async (): Promise<void> => {
await fetch('/api/users/1', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
name: '张三',
email: 'zhangsan@example.com',
age: 25 // 必须传所有字段
})
});
};

// PATCH - 部分更新(只传需要修改的字段)
// 其他字段保持不变
const patchUpdate = async (): Promise<void> => {
await fetch('/api/users/1', {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
email: 'new-email@example.com' // 只更新 email
})
});
};

什么是幂等性?

幂等性(Idempotency)指的是:同一个请求执行一次和执行多次的效果完全相同,服务器状态不会因为多次执行而产生不同的结果。

// GET - 幂等 ✅
// 多次获取同一资源,结果一致
// GET /api/users/1 → 总是返回同一个用户

// PUT - 幂等 ✅
// 多次用同样的数据替换,最终结果一致
// PUT /api/users/1 { name: "张三", age: 25 } → 执行多次结果相同

// DELETE - 幂等 ✅
// 删除一次和删除多次,最终效果一样(资源不存在)
// DELETE /api/users/1 → 无论执行多少次,用户 1 都不存在

// POST - 非幂等 ❌
// 每次执行都可能创建新资源
// POST /api/users → 每次调用都创建一个新用户

// PATCH - 非幂等 ❌(严格来说)
// 某些 PATCH 操作可能依赖当前状态
// PATCH /api/users/1 { age: "increment" } → 每次 age 加 1,结果不同
面试加分点

幂等性在 API 设计中非常重要——客户端在网络超时后可以安全地重试幂等请求(GET、PUT、DELETE),而不用担心产生副作用。对于非幂等的 POST 请求,通常需要借助幂等键(Idempotency Key)来避免重复提交。

幂等性在实际项目中的应用

// 使用幂等键防止 POST 请求重复创建
const createOrder = async (orderData: Record<string, unknown>): Promise<Response> => {
const idempotencyKey = crypto.randomUUID(); // 客户端生成唯一标识

return fetch('/api/orders', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Idempotency-Key': idempotencyKey // 服务端根据此 key 去重
},
body: JSON.stringify(orderData)
});
};

Q7: 常见的 HTTP 状态码及其含义(301 vs 302 vs 307 vs 308、401 vs 403、502 vs 504)

答案

重定向状态码:301 vs 302 vs 307 vs 308

这四个重定向状态码的核心区别在于是否永久是否保持请求方法

状态码名称永久/临时请求方法变化典型场景
301Moved Permanently永久可能变为 GET域名迁移、HTTP → HTTPS
302Found临时可能变为 GET登录跳转、短链接
307Temporary Redirect临时保持原方法POST 表单临时重定向
308Permanent Redirect永久保持原方法POST API 永久迁移
为什么 301/302 会改变请求方法?

历史原因:早期浏览器实现 301/302 时,会将 POST 请求自动改为 GET 再重定向。虽然 RFC 规范并未要求改变方法,但浏览器的行为已成事实标准。因此后来引入了 307/308 来明确保证不会改变请求方法

// 场景 1: 网站从 HTTP 迁移到 HTTPS(301 永久重定向)
// Nginx 配置:
// server {
// listen 80;
// return 301 https://$host$request_uri;
// }

// 场景 2: POST 请求的临时重定向(307)
// 支付表单提交后临时跳转到第三方支付页面
// 必须用 307,否则 POST 数据会丢失
const handlePayment = async (paymentData: Record<string, unknown>): Promise<void> => {
const response = await fetch('/api/payment', {
method: 'POST',
body: JSON.stringify(paymentData),
redirect: 'follow' // 浏览器自动跟随重定向
});
// 如果返回 307,浏览器会用 POST 方法请求新 URL
// 如果返回 302,浏览器可能会用 GET 方法(POST 数据丢失)
};

客户端错误:401 vs 403

状态码名称含义解决方式
401Unauthorized未认证——没有提供有效的身份凭证登录或刷新 Token
403Forbidden无权限——身份已认证,但权限不足联系管理员获取权限
// 模拟 401 和 403 的处理
const handleApiResponse = async (response: Response): Promise<void> => {
switch (response.status) {
case 401:
// 未登录或 Token 过期 → 跳转登录页
console.log('身份未认证,请重新登录');
// 尝试刷新 Token
const refreshed = await refreshToken();
if (!refreshed) {
window.location.href = '/login';
}
break;

case 403:
// 已登录但权限不够 → 提示无权限
console.log('您没有权限执行此操作');
// 比如普通用户访问管理员页面
break;
}
};

const refreshToken = async (): Promise<boolean> => {
try {
const res = await fetch('/api/auth/refresh', { method: 'POST' });
return res.ok;
} catch {
return false;
}
};
简单记忆
  • 401:你是谁?(请先证明身份)
  • 403:我知道你是谁,但你不能做这件事

服务器错误:502 vs 504

状态码名称含义常见原因
502Bad Gateway网关/代理收到了上游服务器的无效响应上游服务挂了、返回了错误格式的响应
504Gateway Timeout网关/代理等待上游服务器超时上游服务处理太慢、网络不通
面试加分点

502 和 504 都涉及网关/代理,通常出现在有 Nginx 反向代理的架构中。排查时的关键区别:

  • 502:先检查上游服务是否存活(systemctl status、端口是否监听)
  • 504:先检查上游服务响应时间,考虑增加 Nginx 的 proxy_read_timeout

Q8: HTTPS 的 TLS 握手过程(简述四次握手、证书验证、对称密钥协商)

答案

HTTPS 在 TCP 三次握手之后,还需要进行 TLS 握手来建立安全连接。以 TLS 1.2 为例,握手可简化为四个阶段

四次握手概览

详细步骤解析

阶段方向关键内容说明
ClientHello客户端 → 服务器TLS 版本、加密套件、Client Random告诉服务器:我支持哪些加密方式
ServerHello服务器 → 客户端选定套件、Server Random、证书告诉客户端:我们用这个加密方式,这是我的证书
密钥交换客户端 → 服务器Pre-Master Secret客户端验证证书后,生成并加密预主密钥
Finished双向加密验证消息双方确认密钥协商成功,开始加密通信

证书验证过程

客户端收到服务器证书后,需要验证证书的合法性:

// 证书验证的核心步骤(伪代码)
interface Certificate {
subject: string; // 证书持有者(域名)
issuer: string; // 颁发机构
publicKey: string; // 公钥
signature: string; // CA 的数字签名
validFrom: Date;
validTo: Date;
}

const verifyCertificate = (cert: Certificate, hostname: string): boolean => {
// 1. 验证证书链:逐级验证直到根证书(浏览器内置信任)
// 服务器证书 → 中间 CA → 根 CA(受信任)
const chainValid = verifyCertificateChain(cert);

// 2. 验证域名:证书中的域名必须匹配请求的域名
const domainValid = cert.subject === hostname
|| matchWildcard(cert.subject, hostname);

// 3. 验证有效期:证书是否在有效期内
const now = new Date();
const dateValid = now >= cert.validFrom && now <= cert.validTo;

// 4. 验证签名:用 CA 的公钥验证证书签名,确保未被篡改
const signatureValid = verifySignature(cert);

// 5. 检查吊销状态:通过 CRL 或 OCSP 检查证书是否被吊销
const notRevoked = checkRevocationStatus(cert);

return chainValid && domainValid && dateValid && signatureValid && notRevoked;
};

// 类型声明(辅助函数)
declare function verifyCertificateChain(cert: Certificate): boolean;
declare function matchWildcard(pattern: string, hostname: string): boolean;
declare function verifySignature(cert: Certificate): boolean;
declare function checkRevocationStatus(cert: Certificate): boolean;

对称密钥协商过程

TLS 握手的核心目标是让双方安全地协商出一个对称密钥用于后续通信:

// 密钥协商过程(简化描述)

// 1. 双方各自生成随机数
const clientRandom: string = generateRandom(); // 客户端随机数(明文传输)
const serverRandom: string = generateRandom(); // 服务器随机数(明文传输)

// 2. 客户端生成预主密钥,用服务器公钥加密后发送
// 只有服务器的私钥才能解密 → 保证安全
const preMasterSecret: string = generateRandom();
const encrypted: string = rsaEncrypt(preMasterSecret, serverPublicKey);
// → 发送给服务器

// 3. 双方用相同的算法,从三个随机数推导出主密钥
// Master Secret = PRF(Pre-Master Secret, Client Random, Server Random)
const masterSecret: string = PRF(preMasterSecret, clientRandom, serverRandom);

// 4. 从主密钥派生出实际使用的会话密钥
// 包括:加密密钥、MAC 密钥、IV 等
const sessionKeys = deriveKeys(masterSecret);

// 后续通信全部使用对称加密(AES),速度快
// 非对称加密(RSA)仅用于交换预主密钥,速度慢但安全

// 类型声明(辅助函数)
declare function generateRandom(): string;
declare function rsaEncrypt(data: string, publicKey: string): string;
declare function PRF(secret: string, ...randoms: string[]): string;
declare function deriveKeys(master: string): Record<string, string>;
declare const serverPublicKey: string;
为什么不全程使用非对称加密?

非对称加密(RSA/ECDHE)计算量大,速度慢,约比对称加密慢 100-1000 倍。所以 TLS 采用混合加密策略:用非对称加密安全地交换对称密钥,然后用对称加密(AES)加密实际数据,兼顾了安全性和性能。

TLS 1.3 的改进

TLS 1.3 将握手从 2-RTT 优化到 1-RTT,并支持 0-RTT 恢复

特性TLS 1.2TLS 1.3
握手延迟2-RTT1-RTT
0-RTT 恢复不支持支持(会话恢复场景)
密钥交换RSA 或 ECDHE仅 ECDHE(前向安全)
加密套件多种(含不安全的)仅保留 5 种安全套件
前向安全可选强制
什么是前向安全(Forward Secrecy)?

即使服务器的私钥在未来被泄露,也无法解密之前已经完成的通信。TLS 1.3 强制使用 ECDHE 密钥交换,每次连接都生成临时密钥对,确保前向安全。

相关链接