首次启动不会自动生成 config.json,需手动执行 config generate;手柄、键盘锁定和 WebSocket 传输依赖 Secure Context(HTTPS);公网串流在严格 NAT 或防火墙下需自建 TURN 服务器或转发 UDP 端口
moonlight-web-stream
MrCreativ3001GPL-3.0活跃维护把 Sunshine 游戏串流搬进浏览器,不用装客户端,打开网页就能远程玩 PC 游戏。
This is a web server which allows you to stream games with sunshine into a web browser.
下载对应平台压缩包并解压后运行 ./web-server;如需配置文件先执行 ./web-server config generateREADME 未说明具体硬件或显存要求;需先安装并运行 Sunshine 作为串流源,服务端为 Rust 编译的 web-server 可执行文件。
把 Sunshine 串流通过 WebRTC 转发到浏览器,让 PC 游戏无需客户端即可在网页里玩
把 Sunshine 游戏串流搬进浏览器,不用装客户端,打开网页就能远程玩 PC 游戏。
核心能力
- 用 WebRTC 将 Sunshine 串流转发到浏览器
- 内置 STUN 并支持配置 TURN 中继
- 支持 WebSocket 传输作为受限网络回退
- 首个登录用户自动成为管理员
- 可配置 HTTPS 证书与端口范围
容易踩的坑
- v3.0.1最新正式版2026-10-26 · 2026-10-26 15:12 UTC
What Changed
- fix dns name resolution not working when starting a stream: https://github.com/MrCreativ3001/moonlight-web-stream/issues/187
Full Changelog: https://github.com/MrCreativ3001/moonlight-web-stream/compare/v3.0.0...v3.0.1
- v3.0.02026-10-22 · 2026-10-22 13:39 UTC
查看更新说明
What's Changed
- Support launch-time stream settings overrides via query parameters by @nako-ruru in https://github.com/MrCreativ3001/moonlight-web-stream/pull/142
- fix bad header length in websocket by @pccr10001 in https://github.com/MrCreativ3001/moonlight-web-stream/pull/139
- fix timeout when pairing by @MrCreativ3001 in https://github.com/MrCreativ3001/moonlight-web-stream/pull/154
- Fix #148: WEBRTC_PORT_RANGE baked into the Docker image overrides config.json by @fisherjoey in https://github.com/MrCreativ3001/moonlight-web-stream/pull/161
- Fix EncodedVideoChunk TypeError on timestamp wrap/reset (negative duration) by @fisherjoey in https://github.com/MrCreativ3001/moonlight-web-stream/pull/163
- Add option to hide the sidebar button during streaming (#155) by @fisherjoey in https://github.com/MrCreativ3001/moonlight-web-stream/pull/162
- Fix: honor moonlight.pair_device_name config (was hardcoded "roth") by @fisherjoey in https://github.com/MrCreativ3001/moonlight-web-stream/pull/158
- Fix in-band VideoDecoder level hints (silent software-decode above ~720p) by @fisherjoey in https://github.com/MrCreativ3001/moonlight-web-stream/pull/159
- Add the option to make username case insensitive for header authorization by @MrCreativ3001 in https://github.com/MrCreativ3001/moonlight-web-stream/pull/165
- Stop the host stream when the client closes the WebSocket by @Tom1tk in https://github.com/MrCreativ3001/moonlight-web-stream/pull/174
- Measure web socket delay against the lowest round trip and drop video in bursts by @Tom1tk in https://github.com/MrCreativ3001/moonlight-web-stream/pull/184
- fix(stream): exit viewer cleanly on graceful host termination by @Greaper88 in https://github.com/MrCreativ3001/moonlight-web-stream/pull/175
- Add macOS release archives by @flashboss in https://github.com/MrCreativ3001/moonlight-web-stream/pull/186
New Contributors
- @fisherjoey made their first contribution in https://github.com/MrCreativ3001/moonlight-web-stream/pull/161
- @Tom1tk made their first contribution in https://github.com/MrCreativ3001/moonlight-web-stream/pull/174
- @Greaper88 made their first contribution in https://github.com/MrCreativ3001/moonlight-web-stream/pull/175
- @flashboss made their first contribution in https://github.com/MrCreativ3001/moonlight-web-stream/pull/186
Full Changelog: https://github.com/MrCreativ3001/moonlight-web-stream/compare/v2.10.0...v3.0.0
See Migrating to v3 for upgrading to the new version.
- v3.0.0-prerelease.10pre-release2026-10-20 · 2026-10-20 09:16 UTC
查看更新说明
What's Changed
- Measure web socket delay against the lowest round trip and drop video in bursts by @Tom1tk in https://github.com/MrCreativ3001/moonlight-web-stream/pull/184
- fix(stream): exit viewer cleanly on graceful host termination by @Greaper88 in https://github.com/MrCreativ3001/moonlight-web-stream/pull/175
New Contributors
- @Greaper88 made their first contribution in https://github.com/MrCreativ3001/moonlight-web-stream/pull/175
Full Changelog: https://github.com/MrCreativ3001/moonlight-web-stream/compare/v3.0.0-prerelease.9...v3.0.0-prerelease.10
- v3.0.0-prerelease.9pre-release2026-10-04 · 2026-10-04 08:12 UTC
- v3.0.0-prerelease.8pre-release2026-10-04 · 2026-10-04 07:44 UTC
查看更新说明
This release is broken
What's Changed
- Stop the host stream when the client closes the WebSocket by @Tom1tk in https://github.com/MrCreativ3001/moonlight-web-stream/pull/174
New Contributors
- @Tom1tk made their first contribution in https://github.com/MrCreativ3001/moonlight-web-stream/pull/174
Full Changelog: https://github.com/MrCreativ3001/moonlight-web-stream/compare/v3.0.0-prerelease.7...v3.0.0-prerelease.8
用户点评
登录用户的真实使用记录,可按版本和硬件筛选还没有人点评。第一个说出真实体验的人最有价值。
Moonlight Web
An unofficial Moonlight Client allowing you to stream your pc to the Web. It hosts a Web Server which will forward Sunshine traffic to a Browser using the WebRTC Api.

Overview
Limitations
- Features that only work in a Secure Context -> How to configure a Secure Context / https
- Controllers: Gamepad API
- Keyboard Lock (allows to capture almost all keys also OS Keys): Experimental Keyboard Lock API
- Web Socket Transport because of the Web Codecs Api
Installation
You can install it manually or with docker
Install Manually
-
Install Sunshine
-
Download the compressed archive for your platform and uncompress it or build it yourself
-
Run the "web-server" executable
-
Go to
localhost:8080and view the web interface. You can also the change bind address.
NOTE A
config.jsonfile is not generated automatically on first startup. If you plan to follow the configuration guides below, you will need to create a config.json with./web-server config generate
Setup
-
Add a new user by typing in your name and password. The first user to login will be created and will be the admin.
-
Add a new pc () with the address as
localhostand leave the port empty (if you've got the default port) -
Pair your pc by clicking on the host (). Then enter the code in sunshine
-
Launch an app
Streaming over the Internet
- Forward the web server port on your router (default is
8080; HTTP is80, HTTPS is443).
You can configure this using thebind_addressoption.
When running on a local network, WebRTC peers usually negotiate connections without any issues.
When streaming over the Internet, the built-in STUN servers will attempt to establish a direct peer-to-peer WebRTC connection. This works for most home networks, but may fail in more restrictive environments (for example, corporate firewalls or strict NAT configurations).
If a direct WebRTC connection cannot be established, you have a few options:
-
Use a TURN server (recommended)
Configure a TURN server to reliably relay WebRTC traffic when direct peer-to-peer connections are blocked. -
Forward ports directly
You can forward the required ports on your router.
Note that this may still fail if the firewall blocks UDP traffic. -
Use WebSocket transport as a fallback
WebSockets use standard HTTP/HTTPS ports and are often allowed even in very restrictive networks, making them a good fallback option when WebRTC negotiation fails. See Using WebSocket Transport.
Configure a turn server
-
Host and configure a turn server like coturn or use other services to host one for you.
-
Add your turn server to your WebRTC Ice Server list
{
"webrtc": {
"ice_servers": [
{
"urls": [
"stun:stun.l.google.com:19302",
"stun:stun1.l.google.com:3478",
"stun:stun.l.google.com:5349"
]
},
{
"urls": [
"turn:yourip.com:3478?transport=udp",
"turn:yourip.com:3478?transport=tcp",
"turn:yourip.com:5349?transport=tcp"
],
"username": "your username",
"credential": "your credential"
}
]
}
}
Some (business) firewalls might be very strict and only allow tcp on port 443 for turn connections if that's the case also bind the turn server on port 443 and add "turn:yourip.com:443?transport=tcp" to the url's list.
Port forward
- Set the WebRTC Port Range to a fixed range
{
"webrtc": {
"port_range": {
"min": 40000,
"max": 40010
}
}
}
-
Forward the port range specified in the previous step as
udp. If you're using Windows Defender make sure to allow NAT Traversal. Important: If your firewall blocks udp connections this won't work and you need to host a turn server -
Configure WebRTC Nat 1 To 1 to advertise your public ip (Optional: WebRTC stun servers can usually automatically detect them):
{
"webrtc": {
"nat_1to1": {
"ice_candidate_type": "host",
"ips": [
"74.125.224.72"
]
}
}
}
It might be helpful to look what kind of nat your pc is behind:
Configuring https
You can configure https directly with the Moonlight Web Server.
- You'll need a private key and a certificate.
You can generate a self signed certificate with this python script generate_certificate.py:
pip install pyOpenSSL
python ./generate_certificate.py
-
Copy the files
server/key.pemandserver/cert.peminto yourserverdirectory. -
Configure the private key and certificate https using the certificates
{
"web_server": {
"certificate": {
"private_key_pem": "./server/key.pem",
"certificate_pem": "./server/cert.pem"
}
}
}
Proxying via Apache 2
It's possible to proxy the Moonlight Website using Apache 2.
Note: When you want to use https, the Moonlight Website should use http so that Apache 2 will handle all the https encryption.
- Enable the modules
mod_proxy,mod_proxy_wstunnel
sudo a2enmod mod_proxy mod_proxy_wstunnel
- Create a new file under
/etc/apache2/conf-available/moonlight-web.confwith the content:
# Example subpath "/moonlight" -> To connect you'd go to "http://yourip.com/moonlight/"
Define MOONLIGHT_SUBPATH /moonlight
# The address and port of your Moonlight Web server
Define MOONLIGHT_STREAMER YOUR_LOCAL_IP:YOUR_PORT
ProxyPreserveHost on
# Important: This WebSocket will help negotiate the WebRTC Peers
<Location ${MOONLIGHT_SUBPATH}/api/host/stream/web_socket>
ProxyPass ws://${MOONLIGHT_STREAMER}${MOONLIGHT_SUBPATH}/api/host/stream/web_socket
ProxyPassReverse ws://${MOONLIGHT_STREAMER}${MOONLIGHT_SUBPATH}/api/host/stream/web_socket
</Location>
ProxyPass ${MOONLIGHT_SUBPATH}/ http://${MOONLIGHT_STREAMER}${MOONLIGHT_SUBPATH}/
ProxyPassReverse ${MOONLIGHT_SUBPATH}/ http://${MOONLIGHT_STREAMER}${MOONLIGHT_SUBPATH}/
- Enable the created config file
sudo a2enconf moonlight-web
- Change config to include the prefixed path
{
"web_server": {
"url_path_prefix": "/moonlight"
}
}
- Use https with a certificate (Optional)
Authentication using a Reverse Proxy
Authentication with a reverse proxy works by the proxy adding custom headers to the request of the user. In this example the username header is named X-Forwarded-User.
Make sure that the header is not changeable by any external request and only the proxy can set this header.
Enable proxy authentication by setting the forwarded header username option. By default the auto create missing user option is turned on even if it's not specified in the config.
{
"web_server": {
"forwarded_header": {
"username_header": "X-Forwarded-User",
"auto_create_missing_user": true
}
}
}
If auto_create_missing_user is enabled, newly created users will be assigned the role configured as the default in the Admin Panel.
Using WebSocket Transport
In some networks (for example, corporate or highly restricted environments), establishing a WebRTC connection can be difficult or may not work at all. To support these cases, you can stream data over WebSockets using the same HTTP or HTTPS port that your server already exposes.
You can force the Web Sockets by setting the Data Transport option to Web Sockets.
There are a few important things to be aware of when using WebSockets for streaming:
-
The browser’s
VideoDecoderAPI is only available when your site is served over HTTPS (a secure context). -
If the application is not running in a secure context, it will fall back to an older video playback API, which may introduce noticeable latency.
Config
The config file is under server/config.json relative to the executable.
Here are the most important settings for configuring Moonlight Web.
Most options have command line arguments or environment variables associated with them.
./web-server help
Generate the config with:
./web-server config generate
For a full list of values look into the Rust Config module.
Bind Address
The address and port the website will run on
Environment Variable: BIND_ADDRESS=0.0.0.0:8080
{
"web_server": {
"bind_address": "0.0.0.0:8080"
}
}
Default User
The user id which is selected by default when providing no login. Go into the Admin Panel and look for the user id of the user you want to make the default.
{
"web_server": {
"default_user_id": 1284358932
}
}
Https Certificates
If enabled the web server will use https with the provided certificate data
Environment Variables:
SSL_PRIVATE_KEY=./server/key.pemSSL_CERTIFICATE=./server/cert.pem
{
"web_server":{
"certificate": {
"private_key_pem": "./server/key.pem",
"certificate_pem": "./server/cert.pem"
}
}
}
WebRTC Port Range
This will set the port range on the web server used to communicate when using WebRTC
Environment Variable: WEBRTC_PORT_RANGE=40000:40010
{
"webrtc": {
"port_range": {
"min": 40000,
"max": 40010
}
}
}
WebRTC Ice Servers
A list of ice servers for webrtc to use.
{
"webrtc": {
"ice_servers": [
{
"urls": [
"stun:stun.l.google.com:19302",
"stun:stun1.l.google.com:3478",
"stun:stun.l.google.com:5349"
]
}
]
}
}
You can also set ice servers with environment variables:
ENV WEBRTC_ICE_SERVER_0_URL=stun:stun.l.google.com:5349
ENV WEBRTC_ICE_SERVER_0_USERNAME=name
ENV WEBRTC_ICE_SERVER_0_CREDENTIAL=cred
ENV WEBRTC_ICE_SERVER_1_URL=stun:stun1.l.google.com:5349
will currespond to the ice server
{
"webrtc": {
"ice_servers": [
{
"urls": [
"stun:stun.l.google.com:19302",
],
"username": "name",
"credential": "cred"
},
{
"urls": [
"stun:stun1.l.google.com:5349"
]
}
]
}
}
You can disable all default ice servers with the cli argument --disable-default-webrtc-ice-servers or the environment variable DISABLE_DEFAULT_WEBRTC_ICE_SERVERS.
WebRTC Ice Server Script
The given script will be executed on every stream start to dynamically generate ice servers.
{
"webrtc": {
"ice_server_script": "./server/ice_server_script.sh"
}
}
The script must return a json list that follows the same structure as the WebRTC Ice Servers config option.
Linux example using a bash script:
#!/bin/bash
# Assign arguments to variables
username="user"
credential="cred"
# Output the json into stdout
echo "[
{
\"urls\": [
\"turn:example.com\"
],
\"username\": \"$username\",
\"credential\": \"$credential\"
}
]"
Windows example using a batch script:
@echo off
:: Assign arguments to variables
set "username=user"
set "credential=cred"
:: Output the JSON with dynamic values
echo [
echo {
echo "urls": [
echo "turn:example.com"
echo ],
echo "username": "%username%",
echo "credential": "%credential%"
echo }
echo ]
WebRTC Nat 1 to 1 ips
This will advertise the ip as an ice candidate on the web server. It's recommended to set this but stun servers should figure out the public ip.
ice_candidate_type:
host-> This is the ip address of the server and the client can connect tosrflx-> This is the public ip address of this server, like an ice candidate added from a stun server.
{
"webrtc": {
"nat_1to1": {
"ice_candidate_type": "host",
"ips": [
"74.125.224.72"
]
}
}
}
You can also use the cli argument --webrtc-nat-1to1-host or environment variable WEBRTC_NAT_1TO1_HOST to use a ip as a host candidate type. This will do the same as the json above.
ENV WEBRTC_NAT_1TO1_HOST=74.125.224.72
Url Path Prefix
This is useful when rerouting the web page using services like Apache 2. Will always append the prefix to all requests made by the website.
Environment Variable: PATH_PREFIX=/moonlight
{
"web_server": {
"url_path_prefix": "/moonlight"
}
}
Forwarded Header Username
The header that will give the authenticated username to this web server.
{
"web_server": {
"forwarded_header": {
"username_header": "X-Forwarded-User"
}
}
}
Forwarded Header Auto Create Missing User
Automatically create a new user when the requested user specified in the username_header is not found.
{
"web_server": {
"forwarded_header": {
"auto_create_missing_user": true
}
}
}
Forwarded Header Ignore Case
Perform a case-insensitive lookup for the user in the in the username_header. If multiple users match the given name, the request will fail.
{
"web_server": {
"forwarded_header": {
"ignore_case": true
}
}
}
Migrating to v2
-
Some config options have changed so backup your old config by renaming it to something like
old_config.json. -
Start the web server which will generate the new config.
-
Move your configurations to the new config
-
The first user to login will be created and will be an admin. All previously stored hosts will be moved to this user.
Other changes:
- Proxy path changed:
- change all instances of
ProxyPass ${MOONLIGHT_SUBPATH}/ http://${MOONLIGHT_STREAMER}/toProxyPass ${MOONLIGHT_SUBPATH}/ http://${MOONLIGHT_STREAMER}${MOONLIGHT_SUBPATH}/ - Proxying via Apache 2
- change all instances of
Migrating to v3
Changes:
- replaced openssl by rustls
- This makes older v1 and v2 server certificates incompatible
- When using a self signed certificate, regenerate it using the updated generate_certificate.py
- replaced moonlight-common-c by moonlight-common-rust
- This could make older Nvidia GameStream and Sunshine Servers unsupported
- moved web socket endpoint from
/api/host/streamto/api/host/stream/web_socket- Change the Web Socket Endpoint when using a Reserve Proxy: See Proxying via Apache2
config.jsonis not generated at first startup and can optionally be used for more granular control- you can generate a config with
./web-server config generate, if required
- you can generate a config with
- moved
default_user_idanddefault_role_idfrom theconfig.jsoninto thedata.jsonfile- go into the admin panel and set the user or role you that you want to be the default to the default at the bottom of the page
- removed old unused
default_settingsvalue in the config - removed
webrtc.network_typessetting in the config
Contributors
Thanks to everyone who contributed to make this software better :).
Building
Clone this repository:
git clone https://github.com/MrCreativ3001/moonlight-web-stream.git
A Rust installation is required.
Moonlight Web consists the web server binary and a web frontend:
Building the Web Server
Run
cargo build --release
.
You can use Cargo Cross for cross compilation:
cross build --release --target YOUR_TARGET
Note: windows only has the gnu target x86_64-pc-windows-gnu. macOS is built natively for aarch64-apple-darwin and x86_64-apple-darwin.
Building the Frontend
Build the web frontend with npm.
To generate bindings to rust code it is using Uniffi Bindgen React Native. This has a dependency on wasm-bindgen.
cargo install wasm-bindgen-cli
npm install
npm run build
The build output will be in dist/.
If you're compiling in:
- debug mode -> the folder needs to be called
dist/ - release mode -> the folder needs to be called
static/