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_HOST是39.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('/'))
但这是"打补丁",根本解决还是靠正确配置代理。
✅ 最终建议
- 检查是否有 Nginx/Apache → 有就加
proxy_set_header Host $http_host; - Django 设置
USE_X_FORWARDED_HOST = True - 重启服务生效
这样 request.build_absolute_uri() 就能正确生成:
http://39.105.138.65:8080/student-volunteer-er/6/?token=xxx
而不是丢失端口的版本。