request.build_absolute_uri()为什么没有获得端口?

Django 没有正确识别请求中的端口号 ,通常是因为 反向代理(如 Nginx、Apache)或负载均衡器 在转发请求时 没有传递原始的 Host,或者 Django 没有正确读取它。


🔍 根本原因分析

虽然你浏览器访问的是:

复制代码
http://39.105.138.65:8080/qr-code/generate/

但你的 Django 应用实际监听的可能是 127.0.0.1:8000 ,而前面有一个 Nginx / Apache / 云服务器安全组 / 容器端口映射8080 转发到了内部端口(比如 8000)。

在转发过程中:

  • 如果 没有设置 proxy_set_header Host $host;
  • 或者 Django 没有启用 USE_X_FORWARDED_HOST

那么 Django 收到的 Host 头就可能是:

复制代码
Host: 39.105.138.65   # ❌ 没有 :8080

而不是:

复制代码
Host: 39.105.138.65:8080   # ✅ 正确

于是 request.build_absolute_uri() 就生成了不带端口的 URL。


✅ 解决方案

第一步:确认是否用了反向代理(如 Nginx)

如果你用了 Nginx,请检查配置是否包含:

复制代码
server {
    listen 8080;

    location / {
        proxy_pass http://127.0.0.1:8000;  # 假设 Django 跑在 8000
        proxy_set_header Host $host;           # 👈 必须有!
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

🔑 关键是 proxy_set_header Host $host;
$host 会保留原始请求的 Host(包括端口,如果有的话)

⚠️ 注意:$host 不包含端口!

要保留端口,应该用 $http_host

复制代码
proxy_set_header Host $http_host;  # ✅ 包含端口(如 39.105.138.65:8080)
  • $host = 域名或 IP(无端口)
  • $http_host = 客户端请求头中的完整 Host(含端口)

✅ 所以正确配置是:

复制代码
proxy_set_header Host $http_host;

第二步:在 Django settings.py 中启用代理支持
复制代码
# settings.py

# 允许从 X-Forwarded-Host 获取主机名
USE_X_FORWARDED_HOST = True

# 如果是 HTTPS(这里你是 HTTP,可忽略)
# SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')

这样 Django 才会信任来自代理的 Host 头。


第三步:验证请求头(调试用)

在你的视图中临时加一段代码,打印请求头:

复制代码
def generate_qr_code(request, student):
    print("=== Request META ===")
    print("HTTP_HOST:", request.META.get('HTTP_HOST'))
    print("SERVER_NAME:", request.META.get('SERVER_NAME'))
    print("SERVER_PORT:", request.META.get('SERVER_PORT'))
    print("X-Forwarded-Host:", request.META.get('HTTP_X_FORWARDED_HOST'))
    print("=====================")
    # ... 继续原有逻辑

访问页面后看日志:

  • 如果 HTTP_HOST39.105.138.65(无端口)→ 说明代理没传端口
  • 如果是 39.105.138.65:8080 → 说明 Django 配置有问题

🚫 如果你没用 Nginx,而是直接 python manage.py runserver 0.0.0.0:8080

那问题更奇怪,因为 runserver 会直接读取 Host 头。

但请注意:

  • 浏览器在访问 标准端口(HTTP 80 / HTTPS 443)时会省略端口
  • 但你用的是 8080(非标准),所以浏览器一定会发送 Host: 39.105.138.65:8080

此时 request.get_host() 应该返回带端口的值。

你可以测试:

复制代码
# 在视图中
print(request.get_host())  # 应该输出 "39.105.138.65:8080"

如果不是,可能是:

  • 云服务器安全组 / 防火墙做了端口转发(如 8080 → 80)
  • Docker 端口映射未正确传递 Host 头

✅ 临时 workaround(不推荐长期使用)

如果你无法改 Nginx 或服务器配置,可以手动拼接端口(仅限固定端口场景):

复制代码
from urllib.parse import urljoin

# 获取基础 URL(不含端口)
base = request.build_absolute_uri('/').rstrip('/')
# 强制加上 :8080
if not base.endswith(':8080'):
    base = base + ':8080'

relative_url = reverse('student_volunteer_erlist', kwargs={'student_id': student.id})
qr_data = urljoin(base + '/', relative_url.lstrip('/'))

但这是"打补丁",根本解决还是靠正确配置代理


✅ 最终建议

  1. 检查是否有 Nginx/Apache → 有就加 proxy_set_header Host $http_host;
  2. Django 设置 USE_X_FORWARDED_HOST = True
  3. 重启服务生效

这样 request.build_absolute_uri() 就能正确生成:

复制代码
http://39.105.138.65:8080/student-volunteer-er/6/?token=xxx

而不是丢失端口的版本。

相关推荐
m0_748554814 小时前
golang如何实现用户订阅偏好管理_golang用户订阅偏好管理实现总结
jvm·数据库·python
smj2302_796826524 小时前
解决leetcode第3911题.移除子数组元素后第k小偶数
数据结构·python·算法·leetcode
偶尔上线经常挺尸5 小时前
《100个“反常识”经验15:Nginx 502排查:从应用到内核》
运维·nginx·性能调优·反向代理·502错误·http排错
阿正呀5 小时前
Redis怎样实现本地缓存的高效失效通知
jvm·数据库·python
yoyo_zzm5 小时前
Laravel9.x新特性全解析
数据库·mysql·nginx
2501_901200535 小时前
mysql如何设置InnoDB引擎参数_优化innodb_buffer_pool
jvm·数据库·python
_.Switch5 小时前
东方财富股票数据JS逆向:secids字段和AES加密实战
开发语言·前端·javascript·网络·爬虫·python·ecmascript
Mr_sst5 小时前
Claude Code 部署与使用保姆级教程(2026 最新)
python·ai
瞎某某Blinder5 小时前
DFT学习记录[6]基于 HES06的能带计算+有效质量计算
python·学习·程序人生·数据挖掘·云计算·学习方法
m0_495496416 小时前
mysql处理复杂SQL性能_InnoDB优化器与MyISAM差异
jvm·数据库·python