Docker容器中Nginx代理Mongo Express资源404问题求助
我搭建了包含web、api、mongodb、mongo-express和nginx的Docker多容器环境,目标是用Nginx实现流量转发。目前访问localhost/api和web服务均正常,但访问localhost/mongo-express时页面加载成功,但其请求的静态资源全部返回404错误。我是Docker新手,完全不熟悉Nginx。
当前的docker-compose配置:
version: '3' services: # Web App Container web: build: context: web container_name: web networks: - mynetwork # API Container api: build: context: api container_name: api networks: - mynetwork # Nginx Container nginx: image: nginx:1.21-alpine container_name: nginx volumes: - ./nginx/nginx.conf:/etc/nginx/nginx.conf ports: - "80:80" - "403:403" depends_on: - web - api - mongodb - mongo-express networks: - mynetwork # Mongodb Container mongodb: image: mongo container_name: mongodb restart: always environment: MONGO_INITDB_ROOT_USERNAME: root MONGO_INITDB_ROOT_PASSWORD: root volumes: - mongodb_data:/data/db networks: - mynetwork # Mongo Express Container mongo-express: image: mongo-express container_name: mongo-express restart: always environment: ME_CONFIG_MONGODB_ADMINUSERNAME: admin ME_CONFIG_MONGODB_ADMINPASSWORD: admin ME_CONFIG_MONGODB_URL: mongodb://root:root@mongodb:27017/ depends_on: - mongodb networks: - mynetwork volumes: mongodb_data: networks: mynetwork:
Nginx配置文件:
# nginx.conf user nginx; worker_processes auto; error_log /var/log/nginx/error.log warn; pid /var/run/nginx.pid; events { worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] \"$request\" ' '$status $body_bytes_sent \"$http_referer\" ' '\"$http_user_agent\" \"$http_x_forwarded_for\"'; access_log /var/log/nginx/access.log main; sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 65; types_hash_max_size 2048; server { listen 80; server_name example.com www.example.com; location / { proxy_pass http://web:8080/; 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; } location /api/ { rewrite ^/api(/.*)$ $1 break; proxy_pass http://api:3000/; # Additional proxy settings for the API if needed } location /mongo-express/ { rewrite ^/mongo-express(/.*)$ $1 break; proxy_pass http://mongo-express:8081/; # Additional proxy settings for Mongo Express if needed } location /mongodb/ { rewrite ^/mongodb(/.*)$ $1 break; proxy_pass http://mongodb:27017/; # Additional proxy settings for MongoDB if needed } error_page 404 /404.html/; location = /40x.html/ { } error_page 500 502 503 504 /50x.html/; location = /50x.html/ { } } }
我原本认为反向代理能正常解析这些资源,尝试过调整Nginx配置、在docker-compose中设置ME_CONFIG_SITE_BASEURL: mongo-express,但均无效。我不仅需要解决方案,还希望理解问题产生的原因。
问题原因
- 静态资源路径不匹配:mongo-express返回的页面中,静态资源(CSS、JS、图片等)使用绝对路径(比如
/css/main.css)。当通过localhost/mongo-express访问时,浏览器会直接请求localhost/css/main.css,这个路径会被Nginx匹配到根路径的web服务,无法找到资源,返回404。 - baseurl配置与转发逻辑冲突:你尝试设置
ME_CONFIG_SITE_BASEURL: mongo-express,但Nginx的rewrite规则把请求前缀/mongo-express/去掉后转发给容器,导致容器生成的带前缀的资源路径(比如/mongo-express/css/main.css)被截断,无法匹配转发规则。
解决方案
提供两种可行方案,任选其一即可:
方案一:同步baseurl与Nginx转发路径(推荐)
让mongo-express生成的资源路径自带代理前缀,和Nginx转发规则完全匹配。
- 修改docker-compose的mongo-express配置:
添加ME_CONFIG_SITE_BASEURL环境变量,注意值要以斜杠结尾:mongo-express: image: mongo-express container_name: mongo-express restart: always environment: ME_CONFIG_MONGODB_ADMINUSERNAME: admin ME_CONFIG_MONGODB_ADMINPASSWORD: admin ME_CONFIG_MONGODB_URL: mongodb://root:root@mongodb:27017/ ME_CONFIG_SITE_BASEURL: /mongo-express/ # 必须带结尾斜杠 depends_on: - mongodb networks: - mynetwork - 修改Nginx的location配置:
去掉rewrite规则,让Nginx完整转发前缀路径到容器:location /mongo-express/ { proxy_pass http://mongo-express:8081/mongo-express/; # 和baseurl保持一致 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; }
原理:ME_CONFIG_SITE_BASEURL会让mongo-express生成的所有资源路径都带上/mongo-express/前缀,浏览器请求时发送localhost/mongo-express/css/main.css,Nginx直接转发到容器内对应路径,资源即可正常加载。
方案二:用Nginx替换页面中的资源路径
如果不想修改mongo-express的配置,可以用Nginx的sub_filter模块,把页面里的绝对路径替换成带代理前缀的路径:
修改Nginx的location配置:
location /mongo-express/ { rewrite ^/mongo-express(/.*)$ $1 break; proxy_pass http://mongo-express:8081/; 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; # 替换页面中所有绝对路径的前缀 sub_filter 'href="/' 'href="/mongo-express/'; sub_filter 'src="/' 'src="/mongo-express/'; sub_filter_once off; # 确保替换所有匹配项 }
原理:Nginx先去掉前缀转发请求给mongo-express,再把返回页面里的href="/和src="/替换成href="/mongo-express/,浏览器会请求带前缀的资源路径,被当前location匹配并转发给容器。
内容的提问来源于stack exchange,提问作者Dave

