
1. 从一次“神秘”的502错误说起那天下午我正调试一个前后端分离的项目前端页面突然就白了控制台里赫然躺着一行刺眼的红字unexpected status 502 bad gateway: unknown error, url: http://127.0.0.1:1572。相信不少朋友都见过类似的报错从404 Not Found到502 Bad Gateway再到418 I‘m a teapot是的这是个真实的状态码这些由HTTP协议定义的状态码就像服务器给我们留的“摩斯电码”读懂它们是定位线上问题的第一步。而这一切的基石就是HTTP和它的安全升级版HTTPS。很多人觉得HTTP/HTTPS就是“网址开头的那串http或https”顶多再加个“安全”的印象。但当你真正去排查一个网络问题比如为什么登录状态总是丢Cookie/Session问题为什么爬虫抓不到数据请求方法被限制或者为什么自己的API总被劫持缺乏HTTPS加密时你会发现对这一套基础协议的理解深度直接决定了你解决问题的效率。它不仅仅是前端向后端发个请求那么简单而是一整套涵盖了传输格式、交互方法、状态反馈、安全加固和状态管理的完整通信契约。今天我就结合自己这些年踩过的坑把HTTP和HTTPS那点事掰开揉碎了讲清楚。我们会从最原始的报文格式开始看数据到底是怎么“打包”和“拆包”的然后聊聊八种请求方法各自该在什么场合下使用接着解读那些让人眼花缭乱的响应状态码让你下次看到502不再心慌再深入到HTTPS的加密流程看看“小锁头”背后到底发生了多少轮握手最后彻底理清Cookie和Session这对“难兄难弟”的区别与联系。无论你是刚入门的新手还是偶尔会被协议细节卡住的老手这篇内容都能帮你把这块基石打得更牢。2. 拆解HTTP报文网络通信的“信封”与“信纸”如果把一次HTTP通信比作寄信那么HTTP报文就是信封和信纸本身。它规定了信息必须以什么样的格式书写和封装对方才能正确解读。报文分为两种请求报文客户端发给服务器和响应报文服务器回给客户端。它们的结构非常相似都包含三个部分起始行、头部字段和消息主体。2.1 请求报文你想要什么以及你是谁一个典型的HTTP请求报文长这样GET /api/user?id123 HTTP/1.1 Host: api.example.com User-Agent: Mozilla/5.0 Accept: application/json Cookie: sessionIdabc123第一行是请求行这是请求报文的灵魂。它由三部分组成用空格分隔请求方法比如GET。它定义了本次操作的类型是“获取”还是“提交”我们下一章会详细讲八种方法。请求目标通常是URL的路径和查询部分比如/api/user?id123。它告诉服务器你要操作哪个资源。协议版本最常见的是HTTP/1.1现在也逐渐普及HTTP/2和HTTP/3。版本决定了后续的通信规则。从第二行开始到第一个空行前是请求头。每一行都是一个Key: Value对。头部字段承载了关于这次请求的大量元信息比如Host目标主机。这在HTTP/1.1中是必须的因为一个服务器可能托管多个网站。User-Agent客户端标识。服务器可以据此返回适配的页面但也被常用于爬虫识别。Accept告诉服务器客户端希望接收什么类型的响应内容如application/json。Cookie将之前服务器通过Set-Cookie头部设置的数据带回给服务器用于维持会话状态。这是实现登录状态的关键。Content-Type和Content-Length在POST、PUT等方法中用于描述消息主体的类型和长度。空行之后是可选的消息主体。GET请求通常没有主体数据通过URL查询字符串传递。而POST、PUT等方法提交表单或JSON数据时数据就放在这里。一个实操中的坑很多新手在写爬虫或者调用API时只关注URL却忽略了请求头。比如有些API必须携带正确的User-Agent或Accept头部否则直接返回406 Not Acceptable或403 Forbidden。用Python的requests库时养成使用headers参数的好习惯能避免很多莫名奇妙的错误。2.2 响应报文服务器给你的答复服务器处理完请求后会发回一个响应报文HTTP/1.1 200 OK Content-Type: application/json; charsetutf-8 Content-Length: 89 Set-Cookie: sessionIdxyz789; Path/; HttpOnly {code: 0, data: {name: 张三}, message: success}第一行是状态行同样由三部分组成协议版本如HTTP/1.1。状态码三位数字如200。这是服务器对你请求结果的“总结陈词”。原因短语对状态码的简短文字描述如OK。人类可读但程序通常只认状态码。紧接着是响应头格式同请求头。一些关键的响应头包括Content-Type响应主体的媒体类型如text/html、application/json。浏览器根据它来决定如何渲染内容。Content-Length响应主体的字节长度。Set-Cookie这是服务器向客户端“种”Cookie的指令。上面例子中服务器要求浏览器保存一个名为sessionId值为xyz789的Cookie并设置了路径和HttpOnly属性防止JavaScript访问增强安全性。Location当状态码是3xx重定向时这个头部指示客户端应该跳转的新地址。空行之后是响应主体即服务器返回的实际数据可以是HTML、JSON、图片字节流等。理解报文格式是底层调试的基础。当你在Chrome开发者工具的Network面板查看一个请求时看到的“Headers”标签页就是这些报文头部经过工具美化后的呈现。而“Preview”或“Response”标签页则是消息主体。下次遇到问题别只看状态码仔细检查一下请求头是否按预期发送响应头是否有特殊的指令如重定向或Cookie设置往往能更快定位问题根源。3. 八种请求方法不只是GET和POSTHTTP/1.1协议定义了八种方法Method有时也叫动作。它们赋予了URL不同的语义指明了应对资源进行何种操作。很多人只熟悉GET和POST但在设计RESTful API或进行更精细的资源管理时其他方法能让你代码的意图更清晰。1. GET获取资源这是最常用的方法用于请求指定的资源。它应该是安全的多次执行不会改变服务器状态和幂等的执行一次和执行多次效果相同。数据通过URL查询字符串传递有长度限制且会在浏览器历史记录和日志中明文显示。所以绝对不要用GET请求执行登录、支付等改变状态的操作。2. POST提交数据创建资源用于向指定资源提交数据通常会导致服务器状态变化如新建订单、发表评论。它既不是安全的也不是幂等的重复提交可能创建多个资源。数据放在请求主体中更适合传输敏感或大量数据。3. PUT替换整个资源用于向指定位置上传其最新内容替换整个目标资源。它是幂等的多次调用产生相同效果。例如用PUT更新用户ID为123的完整信息。4. PATCH部分更新资源用于对资源进行局部修改。与PUT替换整个资源不同PATCH只传递需要改变的字段。它既不是安全的也不一定是幂等的取决于实现。5. DELETE删除资源请求服务器删除指定的资源。它是幂等的删除一次和删除多次结果都是资源不存在。6. HEAD获取资源的元信息与GET方法类似但服务器只返回响应头不返回响应主体。常用于检查资源是否存在、是否被修改通过Last-Modified头或获取文件大小而无需下载整个内容节省带宽。7. OPTIONS查询服务器支持的通信选项用于获取目标资源所支持的通信选项如支持哪些请求方法。在发起跨域请求前浏览器会自动发送一个OPTIONS请求即“预检请求”服务器返回的Access-Control-Allow-Methods头部就指明了允许的方法。8. CONNECT建立隧道用于将连接改为管道方式通常用于通过代理服务器建立SSL隧道即HTTPS连接代理。普通开发中接触较少。9. TRACE追踪路径用于沿着到目标资源的路径执行一个消息环回测试主要用于诊断。由于可能引发“跨站追踪”攻击现代浏览器通常禁用。设计API时的经验之谈遵循RESTful风格时URL代表资源名词请求方法代表操作动词。例如GET /users获取用户列表POST /users创建新用户GET /users/123获取ID为123的用户PUT /users/123替换更新用户123的全部信息PATCH /users/123部分更新用户123的信息DELETE /users/123删除用户123 这样设计API的意图一目了然。我曾见过用POST /users/delete来删除用户的API这在语义上就混乱了也浪费了HTTP协议本身的能力。4. 响应状态码服务器的“表情包”状态码是响应报文的第一印象。它是一个3位数字代码第一位定义了响应的类别1xx信息性状态码请求已接收继续处理。例如101 Switching Protocols切换协议用于WebSocket升级。2xx成功状态码请求被成功处理。最常见的是200 OK。还有201 Created资源创建成功204 No Content成功但无返回内容。3xx重定向状态码需要客户端进一步操作以完成请求。301 Moved Permanently永久重定向302 Found临时重定向但HTTP/1.0规范描述是“Moved Temporarily”导致语义模糊304 Not Modified资源未修改使用缓存。4xx客户端错误状态码客户端请求有误。400 Bad Request笼统的请求错误401 Unauthorized需要认证字面是“未授权”但通常指“未认证”403 Forbidden服务器理解请求但拒绝执行真正的“未授权”404 Not Found资源不存在418 I‘m a teapot彩蛋状态码来自一个愚人节玩笑。5xx服务器端错误状态码服务器处理请求时出错。500 Internal Server Error笼统的服务器内部错误502 Bad Gateway作为网关或代理的服务器从上游服务器收到无效响应503 Service Unavailable服务暂时不可用可能过载或维护504 Gateway Timeout网关超时。重点解读几个高频“坑”码401 vs 403这是最容易混淆的一对。简单记401是“你是谁请先登录认证”。403是“我知道你是谁但你没权限授权访问这个”。遇到401检查登录态Cookie/Session/Token遇到403检查用户角色和权限配置。502 Bad Gateway文章开头提到的错误。这通常不是你代码的直接错误而是你的服务如Nginx作为反向代理在向后端应用服务器如Tomcat、Node.js请求时后端服务器挂了、崩溃了或没响应。排查思路应该是1. 检查后端进程是否存活2. 检查后端服务日志是否有错误3. 检查网络连通性和防火墙规则4. 检查代理服务器如Nginx配置中的上游地址是否正确。504 Gateway Timeout与502类似但更明确是“超时”。代理服务器在规定时间内没收到后端响应。需要检查后端服务的性能是否存在慢查询、死锁或者适当调大代理服务器的超时配置。304 Not Modified这不是错误而是性能优化。当客户端浏览器在请求头中带了If-Modified-Since或If-None-MatchETag服务器会比对资源修改时间或哈希值。如果未变就返回304告诉客户端“直接用本地缓存吧”省去了传输响应主体的开销。这是Web性能优化的重要手段。理解状态码能让你在故障告警第一时间就对问题方向有个基本判断而不是盲目地翻看应用日志。5. HTTPS加密流程从“明文寄信”到“武装押运”HTTP是明文传输的就像用明信片寄信途径的每个路由节点路由器、运营商、公共Wi-Fi都能看到内容。这对于传输密码、信用卡号等敏感信息是致命的。HTTPS就是为了解决这个问题而生它相当于给HTTP协议套上了一层“保险箱”。HTTPS HTTP SSL/TLS。TLS是SSL的后续版本现在普遍使用TLS。它的核心目标有三个机密性加密别人看不懂、完整性防篡改数据没被改过、身份认证防冒充确认对方是真正的服务器。整个过程主要分为两个阶段握手阶段非对称加密建立安全通道和通信阶段对称加密传输数据。5.1 TLS握手详解如何安全地交换“保险箱”钥匙假设客户端浏览器要访问https://www.example.comClient Hello客户端向服务器发起连接并发送一个“打招呼”消息。里面包含了客户端支持的TLS版本、支持的加密套件列表如TLS_AES_256_GCM_SHA384、一个客户端随机数Client Random以及一些扩展信息。Server Hello服务器从客户端支持的加密套件中选出一个自己同时也支持的、最安全的套件。然后服务器也生成一个服务器随机数Server Random连同选定的加密套件、自己的证书Certificate一起发送给客户端。这个证书至关重要它由受信任的证书颁发机构签发里面包含了服务器的公钥和域名等信息。客户端验证证书客户端收到证书后会做一系列验证a) 证书是否在有效期内b) 证书的域名是否与正在访问的域名匹配c) 签发证书的CA是否在客户端的信任根证书列表里d) 证书是否被吊销如果任何一步验证失败浏览器就会弹出红色警告。验证通过客户端才信任这个证书进而信任里面的公钥。生成预主密钥客户端再生成一个随机数称为“预主密钥”。然后用服务器的公钥加密这个预主密钥发送给服务器。注意只有持有对应私钥的服务器才能解密它。至此客户端和服务器共享了三个秘密Client Random, Server Random, Pre-master Secret。生成会话密钥客户端和服务器使用相同的算法如PRF利用刚才共享的三个随机数各自独立地计算出一模一样的“主密钥”。再由主密钥派生出后续通信实际使用的对称加密密钥会话密钥和消息认证码密钥。从此之后双方就用这个对称密钥来加密和解密数据了。之所以切换为对称加密是因为它的计算开销远小于非对称加密适合大量数据传输。握手结束双方互相发送一条用会话密钥加密的“Finished”消息验证之前的握手过程是否被篡改。验证通过握手完成安全通道建立。5.2 为什么需要证书和CA核心问题是客户端如何确信收到的公钥真的属于www.example.com而不是一个中间人伪装的这就是证书和CA的作用。CA是一个大家公认的、可信的第三方机构。服务器向CA证明自己对某个域名的所有权CA审核后用自己的私钥对服务器的公钥和域名等信息进行签名生成证书。客户端内置了信任的CA根证书包含CA的公钥因此可以用CA的公钥来验证服务器证书上的签名。只要签名验证通过就说明这个证书是可信的CA颁发的里面的公钥也就是可信的。一个关于HTTPS的常见误解很多人认为HTTPS网站就绝对安全。实际上HTTPS主要保证的是传输过程的安全。如果服务器本身存在安全漏洞如SQL注入、XSS或者用户访问了一个拥有合法证书但内容恶意的钓鱼网站证书只证明“你是谁”不证明“你是好人”那么安全风险依然存在。另外在一些企业内网可能会部署自签名的根证书进行流量审计此时HTTPS的“端到端”加密在用户与企业代理之间被打破这也是为什么有时公司会要求员工安装内部证书。6. Cookie与Session如何记住“你是谁”HTTP是无状态的服务器不会记得上一次请求是谁发的。但现实应用如购物车、登录必须要有状态。Cookie和Session就是两种在无状态协议上实现状态管理的机制。6.1 Cookie客户端的“身份证”Cookie是由服务器通过Set-Cookie响应头发送到客户端浏览器并由浏览器自动保存并在后续对同一服务器的请求中通过Cookie请求头自动带回的一小段文本信息。Cookie的关键属性NameValue键值对。DomainPath定义了Cookie的作用域。浏览器只会向匹配的域名和路径发送Cookie。Expires/Max-Age过期时间。会话Cookie在浏览器关闭时删除持久Cookie会保存到文件直到过期。HttpOnly设置为true时JavaScript无法通过document.cookie访问此Cookie能有效缓解XSS攻击。Secure设置为true时Cookie只会在HTTPS连接中被发送。SameSite用来限制第三方Cookie防止CSRF攻击。值可以是Strict、Lax或None。Cookie的工作流程用户首次登录服务器验证成功。服务器在响应中设置Set-Cookie: sessionIdabc123; Path/; HttpOnly。浏览器收到后将sessionIdabc123保存起来。此后用户访问该网站下的任何页面浏览器都会自动在请求头中带上Cookie: sessionIdabc123。服务器收到sessionId就知道这是用户“张三”。Cookie的优缺点优点实现简单数据存储在客户端减轻服务器压力。缺点容量和数量限制每个域名下的Cookie有数量和大小限制通常每个4KB总数20-50个。安全性问题如果被窃取XSS攻击攻击者可以冒充用户。因此敏感信息如密码绝不应存于Cookie。每次请求都会携带增加网络开销。6.2 Session服务器端的“用户档案”Session是在服务器端保存用户状态信息的机制。每个Session有一个唯一的IDSession ID这个ID通常通过Cookie或URL重写传递给客户端客户端下次请求时再传回来服务器根据这个ID找到对应的Session数据。Session的典型工作流程基于Cookie传递Session ID用户登录服务器验证成功。服务器在内存、数据库或Redis中创建一个Session对象存入用户信息如userId, username。服务器生成一个唯一的Session ID如UUID并将这个ID通过Set-Cookie设置给客户端例如JSESSIONIDxxxxx。浏览器保存这个包含Session ID的Cookie。后续请求浏览器自动带上这个Cookie。服务器从请求中提取Session ID根据ID去存储中查找对应的Session对象从而获取用户信息。Session的优缺点优点相对安全敏感数据存储在服务器端客户端只持有一个无意义的ID。存储容量大可以存储更复杂的对象。缺点服务器开销需要存储所有活跃用户的Session数据对分布式架构是个挑战需要Session共享方案如Redis。依赖Cookie或URL默认依赖Cookie传递ID如果客户端禁用Cookie需要额外的URL重写方案既麻烦又不安全。6.3 Cookie vs Session核心区别与选择特性CookieSession存储位置客户端浏览器服务器端安全性较低易被窃取和篡改较高敏感信息在服务器容量限制有单个约4KB每域名数量有限无受服务器内存/存储限制服务器压力无数据在客户端有需要存储和管理数据跨域支持可设置但有SameSite限制默认不支持依赖Session ID的传递如何选择记住登录状态、追踪用户会话这是Session的经典场景。Cookie只用来存Session ID用户数据存服务器Session中。存储简单的用户偏好如语言设置、主题颜色可以使用Cookie设置较长的过期时间。实现购物车未登录状态可以将购物车信息临时存在Cookie或浏览器的本地存储中用户登录后再合并到服务器端的用户购物车。一个分布式系统中的Session实战坑在单机时代Session存在应用服务器的内存里很简单。但在微服务或集群环境下用户第一次请求落到服务器ASession存在A上第二次请求被负载均衡到服务器BB上没有这个Session用户就“被登出”了。解决方案有几种1.Session粘滞让同一用户的请求总是落到同一台服务器但这破坏了负载均衡的均衡性且服务器宕机时Session会丢失。2.Session复制在服务器间同步Session网络开销大。3.集中式Session存储这是目前最主流和推荐的做法。将Session数据存入一个外部集中存储如Redis或Memcached。所有应用服务器都从这个中央存储读写Session。这样应用服务器就变成了无状态的可以任意水平扩展。在Spring Boot中只需引入spring-session-data-redis依赖并简单配置就能轻松实现。这个坑我早期部署集群时踩过现象就是用户随机性掉线排查了很久才锁定是Session存储问题。