python

关注公众号 jb51net

关闭
首页 > 脚本专栏 > python > Django获取请求IP

Django获取请求IP的正确方法和最佳实践

作者:detayun

这篇文章主要为大家详细介绍了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

踩坑现场

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_IPNginx传递的真实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

解决方案

坑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_FORREMOTE_ADDR! 🎉

提示:如果你的服务在CDN后面(如Cloudflare),记得在CDN控制台开启"传递真实IP"功能,否则 HTTP_X_FORWARDED_FOR 也会是CDN的IP哦!

到此这篇关于Django获取请求IP的正确方法和最佳实践的文章就介绍到这了,更多相关Django获取请求IP内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文