Nginx配置HTTPS后重定向丢失/projectname子路径问题
问题根因
该异常由多层Nginx代理配置缺失、Django子路径与HTTPS代理信任配置不全共同导致,HTTPS切换后新增的外层反向代理子路径转发规则未完整传递请求上下文给后端服务,具体触发点如下:
- 外层反向代理通过
proxy_pass末尾斜杠截断/projectname路径前缀转发给内层服务时,未传递SCRIPT_NAME、X-Forwarded-Proto请求头,Django无法感知自身部署在/projectname子路径下,也无法识别前端请求使用的是HTTPS协议。 - 配置
proxy_redirect off关闭了Nginx的重定向地址自动重写能力,Django自带的admin登录校验、URL末尾斜杠补全等逻辑返回的Location重定向头默认指向域名根路径,Nginx不会对该地址做修改,浏览器拿到响应后直接跳转到根路径。 - 仅通过
sub_filter替换页面内容中="/开头的路径,既覆盖不了响应头中的重定向地址、Cookie路径属性,也覆盖不了所有页面资源引用场景;同时未配置子路径下的静态、媒体资源转发规则,即使页面加载成功,静态资源也会返回404。 - Django侧仅配置
FORCE_SCRIPT_NAME参数,未开启反向代理头信任、未配置静态资源URL前缀,admin等内置模块生成URL、触发重定向时不会自动拼接子路径前缀,切换HTTPS后Django误将请求识别为HTTP请求,进一步导致重定向逻辑异常。 - HTTP切换前服务直接部署在域名根路径,不存在子路径前缀转发逻辑,因此所有路径访问正常。
修复方案
按以下顺序修改配置,修改前无需调整现有Django路由规则,不要在urlpatterns的path、re_path中手动添加/projectname前缀,避免路径重复。
1. 修改外层HTTPS反向代理配置
首先调整/projectname/路径匹配规则,补全请求头传递、重定向重写、Cookie路径修正规则,同时新增子路径下的静态、媒体资源映射:
# 子路径应用转发规则 location /projectname/ { proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header Host $host; # 传递原始请求协议、子路径前缀给后端 proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header SCRIPT_NAME /projectname; # 开启重定向地址替换,将后端返回的根路径重定向自动补全子路径前缀 proxy_redirect ~/(.*)$ /projectname/$1; proxy_set_header Accept-Encoding ""; proxy_read_timeout 1000; proxy_connect_timeout 1000; proxy_send_timeout 1000; proxy_pass http://localhost:100/; # 补全页面内所有根路径引用的替换规则 sub_filter '="/' '="/projectname/'; sub_filter 'src="/' 'src="/projectname/'; sub_filter 'href="/' 'href="/projectname/'; # 修正Cookie路径,避免session等Cookie写入根路径导致子路径应用无法读取 proxy_cookie_path / /projectname/; sub_filter_once off; } # 子路径静态、媒体资源直接映射,不走应用代理提升性能 location /projectname/static/ { alias /home/webapps/projectname/project_name/static/; } location /projectname/media/ { alias /home/webapps/projectname/project_name/media/; }
2. 修改内层100端口监听的Nginx配置
调整根路径代理规则,补全代理头传递逻辑,确保外层传入的HTTPS协议标识、子路径前缀能正常传递给Gunicorn服务:
location /{ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # 传递外层传入的协议、子路径头 proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; proxy_set_header SCRIPT_NAME $http_script_name; proxy_set_header Host $http_host; proxy_redirect off; if (!-f $request_filename) { proxy_pass http://project_name_app; break; } }
3. 修改Django settings.py配置
补全反向代理信任、子路径资源前缀配置,替换原有仅设置FORCE_SCRIPT_NAME的逻辑:
# 子路径前缀配置 FORCE_SCRIPT_NAME = '/projectname' # 信任反向代理传递的头信息 USE_X_FORWARDED_HOST = True SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') # 静态、媒体资源URL前缀,确保模板标签生成的资源地址带子路径前缀 STATIC_URL = '/projectname/static/' MEDIA_URL = '/projectname/media/' # 登录相关路径补全前缀,避免登录跳转异常 LOGIN_URL = '/projectname/login/'
4. 生效验证
- 执行
nginx -t检查内外层Nginx配置语法无误后,执行systemctl reload nginx重载Nginx配置。 - 重启Gunicorn服务加载新的Django配置。
- 清空浏览器缓存(之前的301永久重定向会被浏览器本地缓存,不清空会持续跳转到旧的错误地址)后重新访问验证。
内容的提问来源于stack exchange,提问作者Davood
相关产品推荐
相关产品推荐

