Python socket请求www.google.com返回301但curl返回200的原因
问题核心原因
你遇到的问题根本不是“跳转地址和原请求一致”,是你手写HTTP请求时犯了非常基础的头字段错误:
- 你的TCP连接确实正确连到了
www.google.com的80端口,但HTTP/1.1协议要求请求必须携带Host头,用来告诉服务端当前请求要访问哪个站点(同一个IP可以托管多个域名站点)。 - 你代码里写的
Host头值是google.com,和你实际要访问的www.google.com不是同一个站点。服务端收到请求后,识别到你访问的是不带www的根域,因此返回301状态码,要求跳转到带www的http://www.google.com/地址。你肉眼觉得跳转地址和你请求的地址一样,本质是你忽略了自己写错Host头的事实,产生了认知偏差。
curl/wget 和手写socket代码的行为差异
二者的实现逻辑差异主要在两点:
- 请求头自动填充逻辑
当你用curl/wget请求www.google.com时,工具会自动从你输入的URL中解析出正确的主机名,自动填充匹配的Host: www.google.com请求头,不会出现手写代码时漏写www.前缀的低级笔误。 - 跳转处理逻辑
- wget默认开启3xx跳转自动跟随,收到301/302类状态码时会自动向跳转后的地址发起新请求,直到拿到最终响应。
- curl默认不自动跟随跳转,需要添加
-L参数才会开启该功能。
你测试时拿到200状态码,核心原因是工具第一次发请求时Host头就是正确的,后续哪怕遇到HTTP转HTTPS的跳转,工具也会按内置逻辑自动处理;而你的裸socket代码只发了一次带错误Host头的请求,收完4096字节响应就直接关闭连接,既没写对核心请求头,也没实现任何跳转处理逻辑,自然拿不到正常的200响应。
修复方式
把你代码里的请求头Host字段值改成和目标主机一致即可:
import socket target_host = "www.google.com" target_port = 80 client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client.connect((target_host, target_port)) # 修正Host头为www.google.com client.send(b"GET / HTTP/1.1\r\nHost: www.google.com\r\n\r\n") response = client.recv(4096) client.close() print(response)
修正后你会发现返回的301响应会提示跳转到HTTPS地址,而非之前的同名www地址,就能验证之前的301完全是Host头写错导致的。
注:用裸socket手写HTTP请求需要自己处理所有协议细节,包括头字段校验、编码、跳转、长连接、重定向、异常处理等,成熟的HTTP客户端工具已经把这些容易踩坑的细节全部封装实现,正常业务开发不建议直接从socket层手写HTTP请求。
内容的提问来源于stack exchange,提问作者Nick
相关产品推荐
相关产品推荐

