
使用 http.server.SimpleHTTPRequestHandler 自定义 HTTP 服务器时,若未调用 self.end_headers(),响应头将不会被发送到客户端,导致浏览器无法识别 Content-Type,从而将 HTML 当作纯文本显示。
使用 `http.server.simplehttprequesthandler` 自定义 http 服务器时,若未调用 `self.end_headers()`,响应头将不会被发送到客户端,导致浏览器无法识别 `content-type`,从而将 html 当作纯文本显示。
在 Python 的 http.server 模块中,SimpleHTTPRequestHandler 提供了底层 HTTP 协议处理能力,但其响应流程需严格遵循 HTTP 规范:响应状态行 → 响应头 → 空行(CRLF)→ 响应体。其中,self.end_headers() 的作用正是写入终止响应头的空行(\r\n),并刷新缓冲区——这是浏览器解析响应头的关键分界点。
在原始代码中,虽然调用了 self.send_header() 设置了 Content-Type 和 Content-Length,但缺少 self.end_headers(),导致响应头未真正发出。浏览器收不到任何响应头(包括 Content-Type),只能依据默认行为(如 MIME 类型嗅探或 fallback 到 text/plain)处理响应体,因此 HTML 被显示为源码而非渲染结果。
此外,还有两个易忽略的关键点:
-
Content-Length的值必须是字符串类型(str(len(...))),而非整数,否则会触发TypeError; -
self.wfile.write()前必须确保响应头已结束,否则数据可能被截断或解析错乱。
✅ 正确实现如下:
立即学习“前端免费学习笔记(深入)”;
import http.server
import socketserver
class CustomHandler(http.server.SimpleHTTPRequestHandler):
def do_GET(self):
html_content = "<html><body><h1>Hello, World!</h1></body></html>"
self.send_response(200)
self.send_header('Content-Type', 'text/html; charset=UTF-8')
self.send_header('Content-Length', str(len(html_content)))
self.end_headers() # ✅ 关键:发送响应头终止符
self.wfile.write(html_content.encode('utf-8'))
def main(port_no):
# 推荐显式绑定到 127.0.0.1,增强安全性与可预测性
with socketserver.TCPServer(("127.0.0.1", port_no), CustomHandler) as httpd:
print(f"Serving at http://127.0.0.1:{port_no}")
httpd.serve_forever()
if __name__ == '__main__':
main(8098)⚠️ 注意事项:
- 若省略
self.end_headers(),即使send_header()被多次调用,响应头也永远不会发出; -
send_response()必须在所有send_header()之前调用,否则会抛出AssertionError; - 对于动态内容,建议使用
io.StringIO或模板引擎生成 HTML 后统一编码,避免手动拼接引入编码风险; - 生产环境请勿直接使用
http.server;它仅适用于开发调试,缺乏安全防护、并发优化与 HTTPS 支持。
总结:end_headers() 不是可选步骤,而是 HTTP 响应生命周期中不可或缺的一环——它标志着响应元数据的终结与正文的开始。掌握这一机制,是构建可预期 Web 行为的基础。



















