最稳妥的方案是让 Nginx 负责 SSL 终结,Django 通过 HTTP 或 uWSGI 与 Nginx 通信,这样既简化了 Django 配置,又能利用 Nginx 处理静态资源和加密握手。
先说结论:生产环境建议由 Nginx 统一处理 HTTPS,Django 仅需配合调整安全设置
- 适合:已有 Nginx 反向代理架构的 Django 项目
- 先准备:域名解析生效、服务器 80 和 443 端口开放、WSGI 服务(Gunicorn/uWSGI)已运行
- 验收:浏览器地址栏显示锁标志、HTTP 自动跳转 HTTPS、curl 检查状态码
1. 证书申请与 Nginx 配置边界
推荐使用 Let's Encrypt 免费证书,通过 Certbot 工具自动申请。注意:Certbot 会自动修改 Nginx 配置,若后续手动编辑配置文件,请勿覆盖证书路径。
# 申请证书并自动配置 Nginx(需提前安装 certbot 和 python3-certbot-nginx)
sudo certbot `--nginx` -d yourdomain.com
# 检查 Nginx 配置语法
sudo nginx -t
# 重载 Nginx 使配置生效
sudo systemctl reload nginx
2. Nginx 配置详解(含 HTTP 强制跳转)
Certbot 自动配置可能缺失 HTTP 到 HTTPS 的强制跳转,或需手动调整反向代理参数。确保配置包含以下两个 server 块:
# HTTP 强制跳转 HTTPS
server {
listen 80;
server_name yourdomain.com www.yourdomain.com;
return 301 https://$host$request_uri;
}
# HTTPS 配置与反向代理
server {
listen 443 ssl;
server_name yourdomain.com www.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:8000; # 指向 Gunicorn/uWSGI 监听端口,严禁指向 Django runserver
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme; # 关键:告诉 Django 原始协议
proxy_set_header X-Real-IP $remote_addr;
}
}
3. 生产环境进程管理
Django 开发服务器(runserver)严禁用于生产环境。需使用 Gunicorn 或 uWSGI 启动服务,并配合 systemd 守护进程。
# 示例:使用 Gunicorn 启动(在项目根目录)
gunicorn myproject.wsgi:application `--bind` 127.0.0.1:8000 `--workers` 3
# 示例:systemd 服务配置片段 (/etc/systemd/system/gunicorn.service)
[Service]
User=www-data
Group=www-data
WorkingDirectory=/path/to/your/project
ExecStart=/path/to/venv/bin/gunicorn `--access-logfile` - `--workers` 3 `--bind` unix:/run/gunicorn.sock myproject.wsgi:application
4. Django 安全设置调整
修改 settings.py,启用安全相关选项。若 Nginx 已配置 HTTP 跳转,Django 端可关闭 SECURE_SSL_REDIRECT 以避免逻辑冲突,依赖 SECURE_PROXY_SSL_HEADER 识别协议。
ALLOWED_HOSTS = ['yourdomain.com', 'www.yourdomain.com']
# 若 Nginx 已处理跳转,此处可设为 False,避免重定向循环
SECURE_SSL_REDIRECT = False
# 关键:信任 Nginx 传递的协议头
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
CSRF_COOKIE_SECURE = True # CSRF cookie 仅通过 HTTPS 传输
SESSION_COOKIE_SECURE = True # Session cookie 仅通过 HTTPS 传输
5. 验证与证书续期
1. 命令行检查
使用 curl 命令查看响应头,确认状态码为 301(HTTP 跳转)或 200(HTTPS 访问)。
# 检查 HTTP 跳转
curl -I http://yourdomain.com
# 检查 HTTPS 响应
curl -I https://yourdomain.com
2. 浏览器检查
访问 http://yourdomain.com,确认是否自动跳转到 https://。点击地址栏锁图标,查看证书有效期。
3. 证书续期验证
Let's Encrypt 证书有效期 90 天,需配置自动续期。运行以下命令测试续期脚本是否正常:
sudo certbot renew `--dry-run`
常见坑与排查
1. 无限重定向循环
若浏览器一直刷新,检查 Nginx 是否传递了 X-Forwarded-Proto,以及 Django 的 SECURE_PROXY_SSL_HEADER 配置是否匹配。若 Nginx 已做跳转,Django 的 SECURE_SSL_REDIRECT 建议设为 False。
2. 防火墙端口未开放
云服务器安全组必须放行 80 和 443 端口,否则 Certbot 验证会失败或用户无法访问。
3. 进程权限与 socket 文件
若使用 Unix Socket 通信,确保 Nginx 用户(www-data)有权访问 socket 文件。systemd 服务需指定正确的 User 和 Group。
4. 静态文件 404
开启 HTTPS 后,如果前端资源引用仍为 http://,浏览器可能会拦截混合内容。确保 Django 模板中使用了 {% static %} 标签且 MEDIA_URL 配置正确。
参考来源
- Django Documentation - Security settings: https://docs.djangoproject.com/en/stable/ref/settings/#security
- Nginx Documentation - Configuring HTTPS servers: https://nginx.org/en/docs/http/configuring_https_servers.html
- Certbot Instructions - Nginx on Ubuntu: https://certbot.eff.org/instructions