Skip to content

server.json config ​

Server-side WebGUI settings are stored in config/webgui/server.json.

The file is created automatically on first launch. A config/webgui/server.example.json template is also written for reference. If tokenSecretBase64 is empty, a new secret is generated and saved automatically.

Full example ​

json
{
  "enableTokens": true,
  "tokenTtlSeconds": 900,
  "queryParamName": "webgui_token",
  "tokenSecretBase64": "<auto-generated or your secret>",

  "autoHudOnJoin": false,
  "autoHudUrl": "",

  "mainMenuUrl": "",
  "deathScreenUrl": "",

  "serveBundledPages": true,
  "bundledPagesDir": "web",

  "requireClientMod": false,
  "pageEventsPerSecond": 20,
  "assetBytesPerSecond": 4194304,

  "updateCheckUrl": "",

  "trustedCommandOrigins": []
}

Fields ​

Signed tokens ​

Signed tokens let your web backend verify that a request comes from a genuine WebGUI client.

FieldTypeDefaultDescription
enableTokensbooltrueAttach a signed token to every page URL the mod opens.
tokenTtlSecondsint900Token lifetime in seconds. Minimum enforced by the mod: 60.
queryParamNamestring"webgui_token"Query parameter name used to carry the token.
tokenSecretBase64stringautoBase64 HMAC secret shared with your backend. Auto-generated if empty.

Auto HUD ​

FieldTypeDefaultDescription
autoHudOnJoinboolfalseAutomatically open the HUD overlay when a player joins the server.
autoHudUrlstring""URL to open as the auto HUD. Required when autoHudOnJoin is true.
FieldTypeDefaultDescription
mainMenuUrlstring""URL sent to clients on join for the main menu screen (opened with F6). Empty = key does nothing.

Death screen ​

Replaces the vanilla death screen with a page of your own.

FieldTypeDefaultDescription
deathScreenUrlstring""Page shown instead of the vanilla death screen. Empty = keep the vanilla screen.

The feature is opt-in because that screen holds the player's only way to respawn. When it is set:

  • The page receives a webgui:death event describing who or what killed the player.
  • The page respawns them by calling window.webgui.respawn().
  • Esc does not close the page, exactly as the vanilla screen refuses to close — but if the page fails to load, Esc starts respawning again, so a broken page cannot trap anyone.
  • Players who die while the browser is still starting get the vanilla screen. Nothing is lost; there is simply no browser yet to draw a page in.

The vanilla HUD keeps rendering behind the page, the same way it does behind the vanilla death screen. Give the page an opaque background if you do not want the hotbar showing through.

Pages served by the server itself ​

You do not need a web host to use WebGUI. Drop files in config/webgui/web/ and point any URL setting at them with the webgui: scheme:

json
{ "mainMenuUrl": "webgui:/index.html", "deathScreenUrl": "webgui:/death.html" }

The directory is created on first start with a working index.html in it, so there is something to edit before there is anything to configure.

/webgui reload picks up changes without a restart: everyone online is told about the new files, and a bundled page someone already has open is refreshed in place, so you can edit a file and watch the result. Pages loaded from a web host are left alone — reload them the way you would in a browser.

FieldTypeDefaultDescription
serveBundledPagesbooltrueServe config/webgui/web/ to players. Harmless when the folder is empty.
bundledPagesDirstring"web"Directory, relative to config/webgui. Cannot point outside it.
assetBytesPerSecondint4194304Page bytes one player may pull per second. 0 disables the limit.

Files travel down the connection the player is already on — no port to open, nothing exposed, and it works behind NAT. Each one is addressed by its SHA-256, so a client that already has a file never asks for it again, and a changed file is simply a different name.

Read this before you build a page for it

Pages the server hosts covers the whole feature in detail: the origin your page runs on, why absolute asset paths 404 and how to fix that in each bundler, CORS and WebSockets against your backend, every limit, and a symptom-by-symptom troubleshooting table. Most first-time problems are one entry in it.

External hosting still works exactly as before

http:// and https:// URLs are untouched. Mix freely — a bundled page can load scripts from a CDN, and an externally hosted page can pull images out of the bundled set.

A bundled page has a different origin

It is served from http://127.0.0.1:25580, not from your domain, so your API should name that origin in CORS. Note that the in-game browser has web security disabled by default (a Rinku setting), so a cross-origin fetch works even without it — but a player can turn that off, so configure CORS anyway. Pages can read window.webgui.assetsBase instead of hard-coding the port.

Client version checks ​

FieldTypeDefaultDescription
requireClientModboolfalseRefuse players whose client has no compatible WebGUI, instead of letting them in with a warning.

On join the server tells the client which WebGUI it runs. What happens next depends on the client:

ClientrequireClientMod: false (default)requireClientMod: true
Same protocolNothing — everything works.Nothing.
Older WebGUIJoins; a chat message names both versions.Refused, with both versions in the disconnect message.
No WebGUIJoins; a chat note if the server actually uses pages.Refused, told the mod is required.

Upgrading from 1.7.0 or earlier on NeoForge

Those versions registered their channels as required, so a NeoForge server turned away any client without the exact same WebGUI — reporting it as "Incompatible client! Please use NeoForge <version>", which points at the wrong mod entirely. From 1.7.1 the channels are optional and the player is told what is really wrong. Set requireClientMod: true to keep refusing them, now with a message that says why.

Rate limiting ​

FieldTypeDefaultDescription
pageEventsPerSecondint20Messages one player's pages may send with postToGame per second. 0 disables the limit.

Page events are dispatched on the server thread, and a page is web content — a runaway loop in your own JavaScript, or a hostile page a player was talked into opening, can otherwise send them as fast as the connection allows. Anything over the limit is dropped and logged once per player per burst. Raise it if a legitimate UI of yours is chatty.

Update check ​

FieldTypeDefaultDescription
updateCheckUrlstring""URL that returns version info. Supported formats: {"version":"1.2.3"} or {"tag_name":"v1.2.3","html_url":"..."}. Empty = disabled.

Trusted command origins ​

Pages can run Minecraft commands as the player (see runCommand). Because the webview can load arbitrary web content, this is gated by origin: a command is accepted only from the main frame of an origin listed here.

FieldTypeDefaultDescription
trustedCommandOriginsstring[][]Origins (scheme://host[:port]) whose pages may run commands. Empty = no page may run commands.
json
{ "trustedCommandOrigins": ["https://ui.myserver.example", "http://localhost:3000"] }
  • Commands run with the player's own permissions — exactly as if typed in chat, so there is no privilege escalation.
  • Requests from any other origin (after a redirect, or from an <iframe>) are dropped.
  • The list is sent to the client on join and cleared on disconnect, so trust never carries across servers.
  • Default ports (443/80) are normalized: https://x ≡ https://x:443.

Notes ​

  • If you use signed tokens, your backend must verify them with the same secret defined in tokenSecretBase64.
  • The config path is config/webgui/server.json — note webgui, not webui.