Django获取请求IP的正确方法和最佳实践
最近在项目中遇到一个诡异的问题:使用域名访问能获取到真实IP,但用IP+端口访问时,获取到的却是 127.0.0.1。经过一番折腾,终于搞清楚了原因。今天就来总结一下 Django获取客户端IP的正确姿势!
常见的错误写法
很多同学(包括我自己)一开始会这样写:
# ❌ 错误:'IP' 不是Django的标准META key ip = request.META['IP'] # KeyError! # ❌ 错误:只用REMOTE_ADDR,代理时会出错 ip = request.META['REMOTE_ADDR'] # 代理时返回127.0.0.1
踩坑现场:
- 直连服务器:✅ 能获取到真实IP
- Nginx反向代理:❌ 返回
127.0.0.1 - CDN/WAF:❌ 返回CDN的IP,不是用户真实IP
Django的META中有哪些IP相关的Key?
| META Key | 说明 | 直连 | 代理 | CDN | 优先级 |
|---|---|---|---|---|---|
REMOTE_ADDR | 直连的客户端IP | ✅ 真实IP | ❌ 代理IP(127.0.0.1) | ❌ 最后一跳IP | ⭐⭐ |
HTTP_X_FORWARDED_FOR | 代理链中的真实IP | ❌ 无 | ✅ 真实IP | ✅ 真实IP | ⭐⭐⭐⭐ |
HTTP_X_REAL_IP | Nginx传递的真实IP | ❌ 无 | ✅ 真实IP | ❌ | ⭐⭐⭐ |
HTTP_CLIENT_IP | 备用方案 | ❌ 无 | ✅ | ✅ | ⭐⭐ |
优先级:
HTTP_X_FORWARDED_FOR > HTTP_X_REAL_IP > REMOTE_ADDR
最佳实践:通用获取IP函数
方法1:标准写法(推荐)⭐⭐⭐
def get_client_ip(request):
"""
获取客户端真实IP(支持直连、代理、CDN)
Args:
request: Django的HttpRequest对象
Returns:
str: 客户端真实IP
"""
# 1. 优先从 HTTP_X_FORWARDED_FOR 获取(代理传递的真实IP)
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
if x_forwarded_for:
# 可能有多个IP(如:client, proxy1, proxy2),取第一个
ip = x_forwarded_for.split(',')[0].strip()
else:
# 2. 没有代理,直接用 REMOTE_ADDR
ip = request.META.get('REMOTE_ADDR', '127.0.0.1')
# 3. 过滤内网IP(可选,防止伪造)
if ip.startswith('127.') or ip.startswith('192.168.'):
return '0.0.0.0'
return ip
使用示例:
def my_view(request):
ip = get_client_ip(request)
print(f"客户端IP: {ip}") # 输出:123.45.67.89
return JsonResponse({'ip': ip})
方法2:更严谨的写法(防伪造)⭐⭐⭐⭐
def get_client_ip(request):
"""
获取客户端真实IP(防伪造、支持多层代理)
"""
# 尝试多个可能的IP来源(按优先级排序)
ip_keys = [
'HTTP_X_FORWARDED_FOR',
'HTTP_X_REAL_IP',
'HTTP_CLIENT_IP',
'REMOTE_ADDR',
]
for key in ip_keys:
ip = request.META.get(key)
if ip:
# HTTP_X_FORWARDED_FOR 可能有多个IP,取第一个
if key == 'HTTP_X_FORWARDED_FOR':
ip = ip.split(',')[0].strip()
# 过滤内网IP和无效IP
if ip and not ip.startswith(('127.', '192.168.', '0.')):
return ip
return '0.0.0.0'
方法3:使用中间件(全局生效)⭐⭐⭐⭐⭐
如果你想在所有视图中都能用 request.client_ip,可以写个中间件:
# middleware.py
class ClientIPMiddleware:
"""
中间件:自动在request对象上添加client_ip属性
"""
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
# 获取真实IP
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
if x_forwarded_for:
request.client_ip = x_forwarded_for.split(',')[0].strip()
else:
request.client_ip = request.META.get('REMOTE_ADDR', '127.0.0.1')
response = self.get_response(request)
return response
配置中间件(settings.py):
MIDDLEWARE = [
# ... 其他中间件
'myapp.middleware.ClientIPMiddleware', # ⭐ 添加这行
]
使用:
def my_view(request):
ip = request.client_ip # ⭐ 直接用,超方便!
print(f"客户端IP: {ip}")
return JsonResponse({'ip': ip})
Nginx配置(最优雅的方案)⭐⭐⭐⭐⭐
如果你用Nginx做反向代理,在Nginx层传递真实IP是最优解:
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://127.0.0.1:8000;
# ⭐⭐⭐ 关键:传递真实IP ⭐⭐⭐
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
}
}然后Django中直接用:
# ✅ 现在REMOTE_ADDR就是真实IP了
ip = request.META.get('REMOTE_ADDR')
不同场景对比
| 场景 | 直连 | Nginx代理 | CDN/WAF | 推荐方法 |
|---|---|---|---|---|
REMOTE_ADDR | ✅ 真实IP | ❌ 127.0.0.1 | ❌ CDN IP | ❌ 不推荐 |
HTTP_X_FORWARDED_FOR | ❌ 无 | ✅ 真实IP | ✅ 真实IP | ⭐⭐⭐⭐ |
中间件 request.client_ip | ✅ | ✅ | ✅ | ⭐⭐⭐⭐⭐ |
| Nginx配置 | ✅ | ✅ | ✅ | ⭐⭐⭐⭐⭐ |
常见坑点
坑1:HTTP_X_FORWARDED_FOR可以伪造!
# 恶意请求(伪造IP) curl -H "X-Forwarded-For: 8.8.8.8" http://your-server.com
解决方案:
- 只信任自己的Nginx/CDN(配置白名单)
- 过滤内网IP(
127.*,192.168.*) - 使用
HTTP_X_REAL_IP(Nginx专属,难以伪造)
坑2:多层代理时,HTTP_X_FORWARDED_FOR有多个IP
用户 → 代理1 → 代理2 → 你的服务器
HTTP_X_FORWARDED_FOR = "用户IP, 代理1IP, 代理2IP"
解决方案:
# 取第一个(最左边的是真实IP)
ip = request.META.get('HTTP_X_FORWARDED_FOR').split(',')[0].strip()
坑3:IPv6的IP获取
# IPv6地址示例:2001:0db8:85a3::8a2e:0370:7334 # 同样适用上述方法 ip = get_client_ip(request) # 支持IPv4和IPv6
完整工具类(直接复制用)
# utils.py
class IPUtils:
@staticmethod
def get_client_ip(request):
"""获取客户端真实IP(防伪造、支持代理)"""
for key in ['HTTP_X_FORWARDED_FOR', 'HTTP_X_REAL_IP', 'HTTP_CLIENT_IP', 'REMOTE_ADDR']:
ip = request.META.get(key)
if ip:
if key == 'HTTP_X_FORWARDED_FOR':
ip = ip.split(',')[0].strip()
if ip and not ip.startswith(('127.', '192.168.', '0.')):
return ip
return '0.0.0.0'
# 使用
from myapp.utils import IPUtils
ip = IPUtils.get_client_ip(request)
总结
| 方法 | 代码 | 推荐度 | 适用场景 |
|---|---|---|---|
❌ request.META['IP'] | 错误 | ⭐ | 别用! |
✅ get_client_ip() | 上面的函数 | ⭐⭐⭐⭐⭐ | 通用推荐 |
| ✅ 中间件 | request.client_ip | ⭐⭐⭐⭐⭐ | 全局使用 |
| ✅ Nginx配置 | proxy_set_header | ⭐⭐⭐⭐⭐ | 有Nginx时 |
最终推荐
# ✅ 最简单:复制这个函数就够了
def get_client_ip(request):
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
if x_forwarded_for:
return x_forwarded_for.split(',')[0].strip()
return request.META.get('REMOTE_ADDR', '127.0.0.1')
# 使用
ip = get_client_ip(request)
一句话总结:别用 request.META['IP'],用 HTTP_X_FORWARDED_FOR 或 REMOTE_ADDR! 🎉
提示:如果你的服务在CDN后面(如Cloudflare),记得在CDN控制台开启"传递真实IP"功能,否则 HTTP_X_FORWARDED_FOR 也会是CDN的IP哦!
到此这篇关于Django获取请求IP的正确方法和最佳实践的文章就介绍到这了,更多相关Django获取请求IP内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!


最新评论