Mojo示例WebSocket在HTTPS环境下无法运行,报不安全操作异常
Firefox中运行Mojo示例代码出现"Uncaught DOMException: The operation is insecure"错误的解决方法
在Firefox中运行示例代码时触发以下错误:
Uncaught DOMException: The operation is insecure.
示例代码
启动命令
$ MOJO_LISTEN=http://*:5000 morbo index.pl
Perl应用代码(index.pl)
use Mojolicious::Lite -signatures; # 渲染DATA段中的index.html.ep模板 get '/' => sub ($c) { $c->render(template => 'index'); }; # 模板调用的WebSocket服务,用于提取网站标题 websocket '/title' => sub ($c) { $c->on(message => sub ($c, $msg) { my $title = $c->ua->get($msg)->result->dom->at('title')->text; $c->send($title); }); }; app->start; __DATA__ @@ index.html.ep % my $url = url_for 'title'; <script> const ws = new WebSocket('<%= $url->to_abs %>'); ws.onmessage = function (event) { document.body.innerHTML += event.data }; ws.onopen = function (event) { ws.send('https://mojolicious.org') }; </script>
问题原因
当前使用Nginx作为Mojo的反向代理,浏览器的混合内容安全策略规定:当页面通过HTTPS协议加载时,所有子资源(包括WebSocket连接)必须使用加密协议。示例代码中url_for->to_abs生成的是未加密的ws://地址,违反了安全策略,导致Firefox抛出该错误。
解决方案
1. 配置Nginx支持WSS反向代理
在Nginx的站点配置中添加WebSocket代理规则,确保将wss://请求转发到Mojo的ws://服务:
location /title { proxy_pass http://127.0.0.1:5000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; # 传递协议头给后端应用 proxy_set_header X-Forwarded-Proto $scheme; }
2. 修改WebSocket地址生成逻辑
确保生成的WebSocket地址与当前页面协议匹配(HTTP对应ws,HTTPS对应wss),有两种实现方式:
方式一:前端自动适配协议
修改index.html.ep中的JavaScript代码,动态切换协议:
% my $url = url_for 'title'; <script> // 根据页面协议自动选择ws或wss const wsProtocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; const wsUrl = new URL('<%= $url->to_abs %>'); wsUrl.protocol = wsProtocol; const ws = new WebSocket(wsUrl); ws.onmessage = function (event) { document.body.innerHTML += event.data }; ws.onopen = function (event) { ws.send('https://mojolicious.org') }; </script>
方式二:后端根据代理头设置协议
在Mojo应用中添加钩子,根据Nginx传递的X-Forwarded-Proto头生成正确的绝对URL:
use Mojolicious::Lite -signatures; # 根据反向代理头修正协议 app->hook(before_render => sub ($c) { if (my $proto = $c->req->headers->header('X-Forwarded-Proto')) { $c->req->url->base->scheme($proto); } }); # 原有路由逻辑不变 get '/' => sub ($c) { $c->render(template => 'index'); }; websocket '/title' => sub ($c) { $c->on(message => sub ($c, $msg) { my $title = $c->ua->get($msg)->result->dom->at('title')->text; $c->send($title); }); }; app->start; __DATA__ @@ index.html.ep % my $url = url_for 'title'; <script> const ws = new WebSocket('<%= $url->to_abs %>'); ws.onmessage = function (event) { document.body.innerHTML += event.data }; ws.onopen = function (event) { ws.send('https://mojolicious.org') }; </script>
3. 重启服务
- 重启Nginx使配置生效:
sudo systemctl restart nginx - 重启Mojo应用(morbo开发服务器直接重启即可)
内容的提问来源于stack exchange,提问作者chovy
相关产品推荐
相关产品推荐

