You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Nginx未拦截无效Host header致Django报DisallowedHost错误排查

Django DisallowedHost错误与Nginx配置问题排查

我运行一个基于Django的站点,在应用错误日志中发现如下错误:

django.core.exceptions.DisallowedHost: Invalid HTTP_HOST header: 'badhost.com'. You may need to add 'badhost.com' to ALLOWED_HOSTS.

我原本认为下方的Nginx配置(已精简)能阻止这类请求到达Django应用,尤其是最后一个server块,但显然未生效。我的配置存在什么问题?

我的目标是让Nginx直接拒绝带有无效Host header的请求,或者是否有办法在应用日志中过滤这类错误信息?

我的Nginx配置:

server {
        server_name mysite.com www.mysite.com;
        listen 80;
        return 302 https://$host$request_uri;
}

server {
        server_name mysite.com www.mysite.com;
        root /home/myuser/mysite.com/public/;

        location / {
                try_files $uri @proxy_to_app;
        }

        location @proxy_to_app {
                proxy_pass http://localhost:8001;
                proxy_set_header Host $host;
                proxy_set_header X-Forwarded-For $remote_addr;
        }

        listen 443 ssl; # managed by Certbot
        ssl_certificate /etc/letsencrypt/live/mysite.com/fullchain.pem; # managed by Certbot
        ssl_certificate_key /etc/letsencrypt/live/mysite.com/privkey.pem; # managed by Certbot
}

server {
        listen 80 default_server;
        listen 443 ssl default_server;
        ssl_certificate /etc/letsencrypt/live/mysite.com/fullchain.pem; # managed by Certbot
        ssl_certificate_key /etc/letsencrypt/live/mysite.com/privkey.pem; # managed by Certbot
        return 444;
}

问题分析

你的Nginx配置核心问题是默认拦截的server块位置错误,导致无SNI的HTTPS请求或部分无效Host请求被合法域名的server块处理,最终转发到Django。

Nginx对HTTPS请求的处理逻辑是:如果客户端不支持SNI(老旧浏览器/工具),无法通过SNI选择server块时,会直接使用配置中第一个监听443端口的server块,而非标记了default_server的块。你的配置中,合法域名的HTTPS server块排在默认拦截块前面,导致这类请求被错误转发。


解决方案

1. 修复Nginx配置,拦截所有无效请求

调整server块顺序,让默认拦截块排在最前面,确保所有不匹配合法域名的请求被直接拒绝:

# 优先放置默认拦截块,处理所有无效Host请求
server {
        listen 80 default_server;
        listen 443 ssl default_server;
        ssl_certificate /etc/letsencrypt/live/mysite.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/mysite.com/privkey.pem;
        return 444; # 直接关闭连接,不返回响应
}

# 处理合法域名的HTTP跳转
server {
        server_name mysite.com www.mysite.com;
        listen 80;
        return 302 https://$host$request_uri;
}

# 处理合法域名的HTTPS请求
server {
        server_name mysite.com www.mysite.com;
        root /home/myuser/mysite.com/public/;

        location / {
                try_files $uri @proxy_to_app;
        }

        location @proxy_to_app {
                proxy_pass http://localhost:8001;
                proxy_set_header Host $host;
                proxy_set_header X-Forwarded-For $remote_addr;
        }

        listen 443 ssl;
        ssl_certificate /etc/letsencrypt/live/mysite.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/mysite.com/privkey.pem;
}

修改后执行重载命令生效:

sudo nginx -s reload

2. 可选:在Django中过滤DisallowedHost错误日志

如果需要临时屏蔽这类错误日志,可在Django项目的settings.py中添加日志过滤器:

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'filters': {
        'skip_disallowed_hosts': {
            '()': 'django.utils.log.CallbackFilter',
            'callback': lambda record: 'Invalid HTTP_HOST header' not in record.getMessage()
        }
    },
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
            'filters': ['skip_disallowed_hosts'],
        },
        'file': {
            'class': 'logging.FileHandler',
            'filename': '/var/log/django/app.log',
            'filters': ['skip_disallowed_hosts'],
        },
    },
    'root': {
        'handlers': ['console', 'file'],
        'level': 'INFO',
    },
}

重启Django应用后,DisallowedHost类错误将不再出现在日志中。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 03:08:15