![无法使 websockets 通过 apache HTTPS 代理工作(302 错误)](https://linux22.com/image/693554/%E6%97%A0%E6%B3%95%E4%BD%BF%20websockets%20%E9%80%9A%E8%BF%87%20apache%20HTTPS%20%E4%BB%A3%E7%90%86%E5%B7%A5%E4%BD%9C%EF%BC%88302%20%E9%94%99%E8%AF%AF%EF%BC%89.png)
我无法使用 apache 代理通过 HTTPS 连接到节点实例,从而使 websockets 在节点后端工作。如果没有使用 (apache) http(s) 代理,Websockets 可以正常工作。
我的设置:我有一个带有多个虚拟主机的 apache 服务器。我有一个用于 myserver.com 的 HTTPS 网页,并通过代理在 api.myserver.com 子域中具有 node/express/ws 的 HTTPS API,该代理将请求重定向到在端口 3333 上运行的 node.js 实例(PM2 上的多个实例)。
这是我的子域名的 apache 虚拟主机:
<VirtualHost *:443>
ServerName api.myserver.com
ServerAdmin [email protected]
DocumentRoot /var/www/html/myserver/api
Options -Indexes
SSLEngine on
SSLProtocol all -SSLv2
SSLCipherSuite ALL:!ADH:!EXPORT:!SSLv2:RC4+RSA:+HIGH:+MEDIUM
SSLCertificateFile /etc/apache2/certs/STAR_myserver_co.crt
SSLCertificateKeyFile /etc/apache2/certs/myserver_private_key.pem
SSLCertificateChainFile /etc/apache2/certs/STAR_myserver_co.ca-bundle
SSLProxyEngine On
ProxyPreserveHost On
ProxyRequests Off
# This is for websocket requests
ProxyPass /wss/ wss://localhost:3333/
ProxyPassReverse /wss/ wss://localhost:3333/
# This is for normal requests
ProxyPass / https://localhost:3333/
ProxyPassReverse / https://localhost:3333/
</VirtualHost>
这对于将连接重定向到节点 Express 后端来说很有效。我已经安装了 mod_proxy、mod_proxy_http 和 mod_proxy_wstunnel。
这是 node.js API 后端:首先,我初始化 express、sessions 等。
// express, session and mongodb session storage
var express = require('express')
var session = require('express-session')
var MongoStore = require('connect-mongo')(session)
var app = express()
// configure sessionStore and sessions, mongodb, etc...
// Certificates and credentials for HTTPS server
var fs = require('fs')
var privateKey = fs.readFileSync(__dirname + '/certs/myserver_private_key.pem', 'utf8')
var certificate = fs.readFileSync(__dirname + '/certs/myserver_cert.pem', 'utf8')
var ca = fs.readFileSync(__dirname + '/certs/myserver_ca.pem', 'utf8')
var credentials = {key: privateKey, cert: certificate, ca: ca}
app.enable('trust proxy')
app.set("trust proxy", 1)
然后我使用与 APACHE 相同的证书安全地设置了 HTTPS 服务器:
// Setup HTTPS server
var https = require('https')
var server = https.createServer(credentials, app)
server.listen(appPort, 'localhost', function () {
// Server up and running!
var host = server.address().address
var port = server.address().port
console.log('myserver listening at https://%s:%s', host, port)
})
最后,我设置了 websocket 连接:
// setup Websockets
wss = new WebSocketServer({ server: server })
wss.on('connection', function connection(ws) {
var cookies = cookie.parse(ws.upgradeReq.headers.cookie)
var sid = cookieParser.signedCookie(cookies["connect.sid"], myserver_secret)
// extract user credentials and data from cookie/sid,
// get the session object
sessionStore.get(sid, function (err, ss) {
...
})
})
然后我的客户端只是尝试安全地连接到 websockets(因为作为 HTTPS 应用程序,我不能使用 ws:// 不安全的 websockets 连接):
window.WebSocket = window.WebSocket || window.MozWebSocket
webSocket = new WebSocket('wss://' + location.host + '/wss')
然后我总是得到相同的错误 302:
[Error] WebSocket connection to 'wss://api.myserver.com/wss' failed: Unexpected response code: 302
如果我直接在本地服务器上测试节点实例https://本地主机:3333/它运行完美,并且 websockets 也能正常工作。
有什么办法可以解决这个问题吗?Apache 代理模块进行的 ws 重定向是否存在问题?
答案1
您已经在 Apache 上设置了 HTTPS,因此您已拥有从客户端到物理服务器的安全连接。您正在代理从 Apache 到本地主机上的 node.js 应用程序的连接,因此唯一可能的窃听者已经物理上在您的机器上。您已告诉您的应用程序信任代理和app.enable('trust proxy'); app.set("trust proxy", 1);
那么为什么还要为 node.js 配置 HTTPS?我们使用 apache/nginx/haproxy 作为 Web 应用前端的主要原因之一是 TLS 卸载。
除了获取X-Forwarded-For
包含远程客户端 IP 的标头之外,启用代理信任还可以让X-Forwarded-Proto
您的 Express 应用知道连接是 http 还是 https。因此,Apache 和节点之间的 https 层除了给您的应用带来开销外,几乎没有什么作用。
我无法帮助您使用 Apache 代理 WebSockets。我开始使用 Apache 时,它还是一系列针对 httpd 的补丁。我在 WebSockets 推出之前就退出了,我不能冒险放弃。太多痛苦的回忆。如果您好奇,以下是我使用 haproxy 将 WebSockets 传递给我的一个 express 应用程序的方法:
mode http
option forwardfor
frontend http-in
bind :::80 v4v6
bind :::443 v4v6 ssl crt /etc/ssl/private
http-request set-header X-Forwarded-Proto https if { ssl_fc }
http-request set-header X-Forwarded-Port %[dst_port]
acl is_websocket hdr(Upgrade) -i WebSocket
acl is_websocket hdr_beg(Host) -i ws
redirect scheme https code 301 if !is_websocket !{ ssl_fc }
use_backend websocket_server if is_websocket
backend websocket_server
timeout queue 5s
timeout server 86400s
timeout connect 86400s
server node_app 127.0.0.1:8080
backend ...others...