Docker+Nginx+Cloudflare部署Mattermost遇WebSocket 400错误求助
问题描述
我在EC2服务器上部署了Web应用,并使用Mattermost Docker容器作为聊天系统,以下是我的Docker Compose配置:
version: '3.9' services: br-db: container_name: br-db platform: linux/amd64 image: postgres:15.2-alpine ports: - "54322:5432" restart: always env_file: './pgsql/local/.env.local' volumes: - br-pgsql-data:/var/lib/postgresql/data networks: - br-net br-app: container_name: br-app build: context: ./be args: - DEV=true tty: true restart: unless-stopped working_dir: /app env_file: './be/.env.local' ports: - "8001:8000" environment: CONTAINER_ROLE: app CONTAINER_ENV: local volumes: - ./be/source:/app depends_on: - br-db networks: - br-net br-redis: image: redis:6.2-alpine container_name: br-redis ports: - "6379:6379" restart: always volumes: - redisdata:/data networks: - br-net br-celery: container_name: br-celery build: context: ./be args: - DEV=true command: celery -A app worker -l info tty: true restart: unless-stopped working_dir: /app env_file: './be/.env.local' volumes: - ./be/source:/app depends_on: - br-db - br-redis networks: - br-net mattermost: platform: linux/amd64 image: mattermost/mattermost-team-edition:latest container_name: mattermost ports: - "8065:8065" - "8067:8067" volumes: - ./mattermost/local/config:/mattermost/config:rw restart: unless-stopped depends_on: - br-db - br-app env_file: './mattermost/local/.app.env' networks: - br-net mattermost-db: container_name: mattermost-db platform: linux/amd64 image: postgres:15.2-alpine ports: - "54323:5432" restart: always env_file: './mattermost/local/.db.env' volumes: - mattermost-db-data:/var/lib/postgresql/data networks: - br-net volumes: br-pgsql-data: redisdata: mattermost-db-data: networks: br-net: driver: bridge
以下是名为default.conf.tpl的Nginx配置文件:
server { listen 80; location / { uwsgi_pass br-app:9000; include /etc/nginx/uwsgi_params; client_max_body_size 50M; proxy_read_timeout 300s; } location ~ /api/v4/websocket$ { proxy_set_header Upgrade \$http_upgrade\; proxy_set_header Connection "upgrade"; proxy_set_header Host \$http_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 http://mattermost:8065; } } server { listen 443; location / { uwsgi_pass br-app:9000; include /etc/nginx/uwsgi_params; client_max_body_size 50M; proxy_read_timeout 300s; } location ~ /api/v4/websocket$ { proxy_set_header Upgrade \$http_upgrade\; proxy_set_header Connection "upgrade"; proxy_set_header Host \$http_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 http://mattermost:8065; } }
我的域名为domain.com,客户端尝试连接wss://domain.com/api/v4/websocket时,Nginx日志显示来自Mattermost的400错误,Mattermost容器日志内容如下:
{"timestamp":"2023-12-21 15:48:07.082 Z","level":"info","msg":"Starting websocket hubs","caller":"platform/web_hub.go:95","number_of_hubs":4} {"timestamp":"2023-12-21 15:53:36.287 Z","level":"debug","msg":"websocket.NextReader: closing websocket","caller":"platform/web_conn.go:849","user_id":"","conn_id":"ugc36pt8fir6tr3fphzdxb7u3h","error":"websocket: close 1001 (going away)"} {"timestamp":"2023-12-21 15:53:36.287 Z","level":"debug","msg":"Received HTTP request","caller":"web/handlers.go:173","method":"GET","url":"/api/v4/websocket","request_id":"jgsx4asum7dqxyb996twxnp7ko","user_id":""} {"timestamp":"2023-12-21 16:09:06.191 Z","level":"debug","msg":"websocket.NextReader: client side closed socket","caller":"platform/web_conn.go:845","user_id":"dunbpz13m3bd7yi3pa3hbuj3hh","conn_id":"rwuerwmspfyo3e7e7pwkk83dew"} {"timestamp":"2023-12-21 16:09:06.191 Z","level":"debug","msg":"Received HTTP request","caller":"web/handlers.go:173","method":"GET","url":"/api/v4/websocket","request_id":"jymam8uchtgxzjn4pht8r7zknw","user_id":"dunbpz13m3bd7yi3pa3hbuj3hh"} {"timestamp":"2023-12-21 16:09:18.097 Z","level":"debug","msg":"websocket.NextReader: client side closed socket","caller":"platform/web_conn.go:845","user_id":"dunbpz13m3bd7yi3pa3hbuj3hh","conn_id":"rwuerwmspfyo3e7e7pwkk83dew"} {"timestamp":"2023-12-21 16:09:18.097 Z","level":"debug","msg":"Received HTTP request","caller":"web/handlers.go:173","method":"GET","url":"/api/v4/websocket","request_id":"6er9kbyjdty1dxqstu116sob9y","user_id":"dunbpz13m3bd7yi3pa3hbuj3hh"}
另外说明:域名托管在Cloudflare,使用自定义SSL证书。请问问题出在哪里?
问题分析与解决
1. Nginx配置中的转义字符错误
你的Nginx配置里,Upgrade \$http_upgrade\; 和 Host \$http_host\; 中的反斜杠是多余的,这会导致传递给Mattermost的请求头格式错误,触发400 Bad Request。
修正后的WebSocket location配置:
location ~ /api/v4/websocket$ { proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $http_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 http://mattermost:8065; # 增加WebSocket必需的长连接超时配置 proxy_connect_timeout 7d; proxy_send_timeout 7d; proxy_read_timeout 7d; }
2. Mattermost外部访问配置缺失
Mattermost需要明确知道自身的外部访问地址和协议,否则会拒绝WebSocket连接。需要在./mattermost/local/.app.env中添加以下环境变量:
MM_SERVICESETTINGS_SITEURL=https://domain.com MM_SERVICESETTINGS_ENABLEWEBSOCKETSECURE=true MM_SERVICESETTINGS_TRUSTEDPROXIES=0.0.0.0/0,::/0
SITEURL:指定Mattermost的外部访问地址ENABLEWEBSOCKETSECURE:启用安全WebSocket(wss)支持TRUSTEDPROXIES:信任所有代理请求(适配Cloudflare的IP转发)
3. Nginx 443端口缺少SSL配置
当前443端口配置未指定SSL证书和密钥,会导致HTTPS请求无法正常处理,进而影响wss连接。需补充SSL相关配置:
server { listen 443 ssl; ssl_certificate /path/to/your/custom-cert.pem; ssl_certificate_key /path/to/your/custom-privkey.pem; location / { uwsgi_pass br-app:9000; include /etc/nginx/uwsgi_params; client_max_body_size 50M; proxy_read_timeout 300s; } location ~ /api/v4/websocket$ { proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $http_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 http://mattermost:8065; proxy_connect_timeout 7d; proxy_send_timeout 7d; proxy_read_timeout 7d; } }
4. Cloudflare配置检查
- 确保Cloudflare Network设置中
WebSocket选项已开启 - 确认Cloudflare SSL模式为Full或Full (strict),且EC2上的自定义SSL证书有效
- 添加缓存规则排除
/api/v4/websocket路径,避免WebSocket请求被缓存拦截
5. 容器连通性验证
在EC2服务器上执行以下命令,验证Mattermost容器是否能正常接收请求:
curl http://localhost:8065/api/v4/websocket -i
返回400 Bad Request属于正常情况(缺少WebSocket升级头),如果出现连接超时,需检查容器端口映射或br-net网络的连通性。
内容的提问来源于stack exchange,提问作者Mojtaba Michael
相关产品推荐
相关产品推荐

