配置Nginx反向代理后Apache HTTPD访问异常,求解决方案
我正尝试为Docker化应用添加Nginx,以下是我的docker-compose.yml文件:
version: "3" services: app: build: context: . dockerfile: Dockerfile # ports: # - 3001:3001 volumes: - ./shared:/app/shared depends_on: - db restart: always environment: - MONGO_URL=mongodb://db:27017/db_name - PORT=3001 - FE_ADDRESS=http://192.168.1.152:3000 - SHARE=/app/shared db: image: mongo restart: always volumes: - ./data:/data/db shared: image: httpd:2.4 ports: - 2020:80 volumes: - ./shared:/usr/local/apache2/htdocs/ restart: always nginx: depends_on: - app - shared restart: always build: dockerfile: Dockerfile context: ./nginx ports: - 3050:90
我保留了shared服务的端口映射,确认该服务可通过localhost:2020正常访问。以下是我的Nginx配置文件:
upstream app { server app:3001; } upstream shared { server shared:80; } server { listen 90; location / { proxy_pass http://app; } location /shared { proxy_pass http://shared; } }
我将app和shared配置为上游块,以便在指定路径部署。目前app服务正常,访问localhost:3050/可查看Node.js应用,但访问localhost:3050/shared时会自动重定向到localhost:90/shared,且无法显示内容。
更新:进入Nginx容器执行curl http://shared,得到如下结果,说明容器内部可正常访问shared服务:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> <html> <head> <title>Index of /</title> </head> <body> <h1>Index of /</h1> <ul><li><a href=".gitkeep"> .gitkeep</a></li> <li><a href="answers/"> answers/</a></li> <li><a href="avatars/"> avatars/</a></li> <li><a href="cases/"> cases/</a></li> <li><a href="comments/"> comments/</a></li> </ul> </body></html>
问题原因
访问localhost:3050/shared时,Apache(shared服务)会返回重定向响应——因为当访问路径不带末尾斜杠时,Apache默认会重定向到带斜杠的路径,并且重定向的目标端口会使用Apache自身监听的80端口,但Nginx代理时没有修改这个响应头,导致浏览器收到的重定向地址是localhost:90/shared/,而这个端口在外部是未暴露的,所以无法访问。
另外,当前Nginx的proxy_pass配置在处理/shared路径时,会把完整路径传递给Apache,即Apache收到的请求是/shared,但它的根目录是/usr/local/apache2/htdocs/,这个路径下并没有shared目录,所以即使解决了重定向,也会出现404。
方案一:修改Nginx配置解决重定向与路径问题
调整Nginx的location /shared配置,解决路径传递和重定向头修改的问题:
upstream app { server app:3001; } upstream shared { server shared:80; } server { listen 90; location / { proxy_pass http://app; 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 /shared/ { proxy_pass http://shared/; # 末尾加斜杠,将路径转为/而非/shared 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_redirect http://$host:80/ /shared/; # 修改Apache返回的重定向地址 } # 处理不带末尾斜杠的请求,自动跳转带斜杠的路径 location = /shared { return 302 /shared/; } }
配置说明:
location /shared/末尾加斜杠,确保Nginx将请求路径的/shared/xxx转为/xxx传递给Apache,匹配Apache的根目录结构proxy_redirect指令将Apache返回的http://xxx:80/开头的重定向地址替换为/shared/,避免端口错误location = /shared处理不带斜杠的请求,主动跳转带斜杠的路径,减少Apache的重定向操作
方案二:直接用Nginx提供静态文件服务(更简单)
既然shared目录已经通过挂载卷被app服务访问,完全可以去掉Apache服务,让Nginx直接提供静态文件服务,减少服务依赖:
- 修改
docker-compose.yml,删除shared服务,并给nginx服务添加目录挂载:
version: "3" services: app: build: context: . dockerfile: Dockerfile volumes: - ./shared:/app/shared depends_on: - db restart: always environment: - MONGO_URL=mongodb://db:27017/db_name - PORT=3001 - FE_ADDRESS=http://192.168.1.152:3000 - SHARE=/app/shared db: image: mongo restart: always volumes: - ./data:/data/db nginx: depends_on: - app restart: always build: dockerfile: Dockerfile context: ./nginx ports: - 3050:90 volumes: - ./shared:/usr/share/nginx/html/shared # 挂载shared目录到Nginx的静态文件目录
- 修改Nginx配置,去掉
shared上游块,直接用location提供静态文件:
upstream app { server app:3001; } server { listen 90; location / { proxy_pass http://app; 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 /shared/ { root /usr/share/nginx/html; autoindex on; # 开启目录索引,和之前Apache的效果一致 } }
这种方案更简洁,减少了一个服务容器,而且Nginx处理静态文件的效率比Apache更高,同时避免了代理带来的重定向问题。
内容的提问来源于stack exchange,提问作者Borislav Stefanov

