Docker Compose部署Flask+Gunicorn+Nginx时非根路由404问题排查
Nginx反向代理Flask应用非根路由404问题解决
问题概述
通过Docker Compose部署的Flask应用(Gunicorn运行),经Nginx反向代理后,根路由/可正常访问,但/dataset_detail/1等动态路由返回404错误。Nginx日志显示,系统尝试从本地静态文件目录/usr/share/nginx/html加载这些路由,而非转发至Flask应用。
错误日志
nginx-1 | 172.25.0.1 - - [22/May/2024:15:57:42 +0000] "GET / HTTP/1.1" 200 5086 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:126.0) Gecko/20100101 Firefox/126.0" nginx-1 | 2024/05/22 15:57:49 [error] 30#30: *1 open() "/usr/share/nginx/html/dataset_detail/1" failed (2: No such file or directory), client: 172.25.0.1, server: localhost, request: "GET /dataset_detail/1?page=1 HTTP/1.1", host: "0.0.0.0", referrer: "http://0.0.0.0/" nginx-1 | 172.25.0.1 - - [22/May/2024:15:57:49 +0000] "GET /dataset_detail/1?page=1 HTTP/1.1" 404 153 "http://0.0.0.0/" "Mozilla/5.0 (X11; Linux x86_64; rv:126.0) Gecko/20100101 Firefox/126.0"
当前Nginx配置
events { worker_connections 1024; multi_accept on; } http { server { listen 80; server_name localhost; # Serve static files directly if possible location /static { alias /usr/share/nginx/html/static; } # Additional static file directories location /docs { alias /usr/share/nginx/html/docs; index index.html; } location /dataset { alias /usr/share/nginx/html/dataset; autoindex on; } # Handle Flask app routes location / { try_files $uri $uri/ @flask; } location @flask { proxy_pass http://app:5000; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_pass_request_body on; } } }
问题原因
- 静态目录Location的前缀匹配冲突:Nginx的普通前缀匹配会优先选择最长的匹配路径。
location /dataset作为前缀匹配规则,会捕获所有以/dataset开头的请求(包括/dataset_detail/1),并尝试通过alias从本地文件系统读取对应路径,而这些动态路由的文件显然不存在,因此返回404。 - 动态路由未进入转发逻辑:由于上述匹配冲突,
/dataset_detail/1这类请求根本没有到达处理Flask转发的location /块,自然不会被转发至后端应用。
解决方案
修改静态目录的Location规则,使用^~修饰符限定精确前缀匹配,避免误捕获动态路由;同时确保动态路由能进入转发逻辑:
修改后的Nginx配置
events { worker_connections 1024; multi_accept on; } http { server { listen 80; server_name localhost; # 使用^~限定静态目录的精确前缀匹配,避免误匹配动态路由 location ^~ /static/ { alias /usr/share/nginx/html/static/; } location ^~ /docs/ { alias /usr/share/nginx/html/docs/; index index.html; } location ^~ /dataset/ { alias /usr/share/nginx/html/dataset/; autoindex on; } # 处理所有动态路由请求 location / { try_files $uri $uri/ @flask; } location @flask { proxy_pass http://app:5000; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_pass_request_body on; } } }
关键修改说明
^~修饰符:添加后,Nginx会优先匹配该规则,且仅当请求路径完全以指定前缀(如/dataset/)开头时才会触发静态文件处理,避免/dataset_detail这类路径被误匹配。- 路径末尾添加斜杠:静态目录的Location和Alias路径都添加末尾斜杠,确保路径拼接时不会出现错误(如避免
/datasetdetail这类错误路径)。 - 动态路由转发逻辑:所有未匹配静态目录的请求会进入
location /,通过try_files尝试本地文件后,自动转发至@flask后端服务。
验证方法
- 重启Nginx容器:
docker-compose restart nginx - 访问动态路由(如
/dataset_detail/1),确认返回正常响应而非404。
内容的提问来源于stack exchange,提问作者AayushPokharel
相关产品推荐
相关产品推荐

