You need to enable JavaScript to run this app. Django生产环境下密码重置与注册时报OSError 99无法分配请求地址
优惠活动
大模型
产品
解决方案
定价
更多

Django生产环境下密码重置与注册时报OSError 99无法分配请求地址

问题根因说明

[Errno 99] Cannot assign requested address 是类Unix系统的原生socket级错误,本质是进程尝试对外建立TCP的SMTP连接时,操作系统内核无法为该连接分配有效的本地网络资源。因为错误触发点完全集中在邮件发送环节、且仅生产环境复现,可以完全排除URL路由配置、Django业务代码逻辑的问题,所有排查方向都聚焦在生产服务器网络栈与SMTP出站连接的匹配性上。

排查与解决步骤

1. 先验证生产服务器的原生SMTP连通性

直接登录生产服务器终端,执行连通性测试:

nc -zv smtp.gmail.com 587
  • 如果执行该命令时,直接抛出和Django中完全一致的Errno 99,说明问题完全出在系统网络层,和Django本身配置无关
  • 对应常见场景的解决方法:
    • 服务器配置了多网卡、多IP,出站路由绑定的源IP是已经被释放/不存在的地址:执行ip route show查看默认出站路由的绑定源地址,修正为当前服务器实际可用的网卡IP
    • DNS解析优先返回smtp.gmail.com的IPv6地址,但服务器没有配置可用的公网IPv6网络:执行nslookup smtp.gmail.com查看解析结果,若IPv6地址排在优先位,可在系统层面强制DNS优先使用IPv4解析,或直接将settings.py中的EMAIL_HOST替换为smtp.gmail.com实际对应的IPv4地址
    • 服务器之前配置的全局出站代理已失效:修正或临时关闭代理配置后重试

2. 排查本地临时端口资源耗尽问题

生产服务器长期运行未重启时,大量已完成的SMTP连接会处于TIME_WAIT状态,如果系统允许分配的本地出站临时端口范围过小,就会因没有可用端口分配给新的SMTP连接触发该错误。

  • 排查命令:
    # 统计当前TIME_WAIT状态的连接总量
    ss -s | grep -i time-wait
    # 查看系统允许的本地出站临时端口范围
    cat /proc/sys/net/ipv4/ip_local_port_range
    
  • 对应解决:
    • 若端口范围过窄(比如默认配置仅为32768-32768这类极小区间),修改/etc/sysctl.conf全局配置:
      net.ipv4.ip_local_port_range = 1024 65535
      net.ipv4.tcp_tw_reuse = 1
      
    • 执行sysctl -p让配置立即生效,无需重启业务服务

3. 显式为SMTP连接指定合规的本地出口IP

部分生产环境的安全策略要求,所有对外业务连接必须显式绑定指定的出口本地IP,不允许内核自动选择源地址。此时Django默认的SMTP后端会因随机选择的源地址不符合系统安全规则,触发地址绑定错误。

  • 解决方式:自定义一个继承默认SMTP后端的类,在初始化SMTP连接时,显式传入当前服务器允许的出口IP作为本地源地址:
    # 路径示例:myapp/utils/email_backend.py
    from django.core.mail.backends.smtp import EmailBackend
    
    class RestrictedSmtpBackend(EmailBackend):
        def open(self):
            if self.connection is None:
                try:
                    self.connection = self.connection_class(
                        host=self.host,
                        port=self.port,
                        timeout=self.timeout,
                        # 替换为生产服务器实际允许的出口内网IP,0表示由系统自动分配可用本地端口
                        local_source_address=("192.168.x.x", 0)
                    )
                # 保留和默认后端完全一致的错误处理逻辑
                except Exception:
                    self.close()
                    raise
    
  • 修改settings.py中的后端配置:
    EMAIL_BACKEND = "myapp.utils.email_backend.RestrictedSmtpBackend"
    
常见误区澄清
  • 该错误和你提供的密码重置URL路由配置完全无关,路由仅负责将请求映射到对应视图,错误发生在视图内邮件发送的系统socket调用阶段
  • 该错误和邮箱账号密码、SMTP端口加密配置无关:如果是认证或加密配置错误,会在TCP连接建立完成后抛出SMTP协议层错误(如535认证失败、STARTTLS协商失败),不会在socket绑定阶段就触发系统级错误
  • 开发环境无此问题的核心原因:开发机通常是单网卡配置、无强制出口IP绑定规则、IPv6网络配置完整,不存在上述生产环境特有的网络层限制

内容的提问来源于stack exchange,提问作者miruni

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.31 02:36:08