mcstatus-httpd shows any information of a Minecraft Server via HTTP-Server as JSON. It uses mcstatus.
Features:
- Minecraft Java Edition and/or Bedrock Edition (a server that is not configured is omitted from the output)
- Java: full player list and plugins via Query, automatic fallback to the normal status ping if Query is disabled
- Bedrock: NetherNet (default since Bedrock 1.26.50) and RakNet (legacy), both are tried at the same time by default
- Both servers are queried in parallel, results are cached (protects the Minecraft server)
- Optional player avatars (Java, head or body, flat or 3D), loaded via mcstatus-httpd with cache and provider fallback
- Optional sub path (
HTTP_PREFIX), e.g. for a reverse proxy - Optional HTTPS
- Built-in health check command
--healthcheckfor docker-compose (also works with the distroless image) - Configuration via environment variables and/or command line arguments
This is a personal project, made for my own Minecraft servers.
Feel free to use it :-), but there is no support and no guarantee.
Note
This project is developed with AI assistance, all changes are reviewed and tested by me.
I use Minecraft Server with Docker:
Examples for docker-compose (minecraft.yml, minecraft-bedrock.yml) you can find here: click
- Python >= 3.12
- mcstatus >= 14.2.0
- Minecraft Java: Query should be enabled (
enable-query=trueinserver.properties).
Without Query the status ping is used as fallback, which only provides a player sample (max. ~12 names) and no plugins and map name. - Minecraft Bedrock with NetherNet: the server port must be reachable via TCP (status via
GET /v1/join).
With RakNet (Bedrock server settingtransport=raknet): the server port must be reachable via UDP.
Every setting can be set as environment variable or as command line argument (arguments take precedence). See python3 mcstatus-httpd.py --help.
| Environment variable | Argument | Default | Description |
|---|---|---|---|
MINECRAFT_JAVA_SERVER |
--java-server |
(disabled) | Java server address |
MINECRAFT_JAVA_SERVER_PORT |
--java-port |
25565 |
Java game port (TCP) |
MINECRAFT_JAVA_SERVER_PORT_QUERY |
--java-query-port |
same as game port | Java query port (UDP) |
MINECRAFT_JAVA_SERVER_OVERWRITE |
--java-overwrite |
same as server | Hostname shown in the output |
MINECRAFT_BEDROCK_SERVER |
--bedrock-server |
(disabled) | Bedrock server address |
MINECRAFT_BEDROCK_SERVER_PORT |
--bedrock-port |
19132 |
Bedrock game port (NetherNet: TCP, RakNet: UDP) |
MINECRAFT_BEDROCK_SERVER_OVERWRITE |
--bedrock-overwrite |
same as server | Hostname shown in the output |
MINECRAFT_BEDROCK_TRANSPORT |
--bedrock-transport |
auto |
nethernet (TCP, default since Bedrock 1.26.50), raknet (UDP, legacy) or auto (both at the same time) |
MINECRAFT_TIMEOUT |
--timeout |
3 |
Seconds until a server counts as offline |
CACHE_TTL |
--cache-ttl |
10 |
Seconds a result is reused, 0 = query on every request |
HTTP_HOST |
--http-host |
0.0.0.0 |
Listen address, 0.0.0.0 = IPv4 only, :: = IPv4 + IPv6 |
HTTP_PORT |
--http-port |
8080 |
Listen port |
HTTP_PREFIX |
--http-prefix |
(none) | Sub path, e.g. mcstatus → /mcstatus |
HTTP_CORS_ORIGIN |
--http-cors-origin |
* (all) |
Websites allowed to load the status (CORS), e.g. https://forum.example.com,https://example.com |
HTTP_TRUSTED_PROXIES |
--http-trusted-proxies |
(none) | Reverse proxies (IPs/networks, e.g. 172.18.0.0/16) whose X-Forwarded-For/X-Real-IP header is used for the client IP in the log |
HTTPD_SSL_ENABLE |
--ssl / --no-ssl |
0 |
Enable HTTPS (1/true/yes/on) |
SSL_CERTFILE |
--ssl-certfile |
ssl.crt |
TLS certificate file |
SSL_KEYFILE |
--ssl-keyfile |
ssl.key |
TLS private key file |
AVATAR_ENABLE |
--avatar / --no-avatar |
0 |
Player avatars (Java), see Player avatars |
AVATAR_TYPE |
--avatar-type |
head |
Default type: head, body, head3d or body3d |
AVATAR_IMAGE_SIZE |
--avatar-image-size |
64 |
Default image width in pixels requested from the providers (8-300), body images are twice as high |
AVATAR_ALLOWED_TYPES |
--avatar-allowed-types |
head,body,head3d,body3d |
Types that can be requested with ?type= (the default type is always allowed) |
AVATAR_ALLOWED_SIZES |
--avatar-allowed-sizes |
16,32,48,64,96,128,192,256,300 |
Sizes that can be requested with ?size= (8-300, the default size is always allowed) |
AVATAR_PROVIDERS |
--avatar-providers |
mc-heads,minotar,crafthead |
Avatar providers in order of preference (fallback), built-in names or own URL templates |
AVATAR_CACHE_TTL |
--avatar-cache-ttl |
3600 |
Seconds an avatar is used before it is loaded again (the old one is kept if loading fails) |
AVATAR_CACHE_TYPE |
--avatar-cache-type |
memory |
memory (lost on restart) or file |
AVATAR_CACHE_DIR |
--avatar-cache-dir |
/tmp/mcstatus-httpd/avatars |
Directory of the file cache |
AVATAR_CACHE_MAX_ENTRIES |
--avatar-cache-max-entries |
500 |
Maximum number of cached avatars (every type and size counts), 0 = unlimited |
AVATAR_TLS_VERIFY |
--[no-]avatar-tls-verify |
1 |
0 = accept self-signed HTTPS certificates of own URL templates (built-in providers are always verified) |
AVATAR_ONLINE_ONLY |
--[no-]avatar-online-only |
1 |
Only serve avatars of online players, 0 = any valid player name (see below) |
AVATAR_TYPE_FALLBACK |
--[no-]avatar-type-fallback |
1 |
Serve the flat avatar if a 3D avatar is not available (head3d → head, body3d → body) |
PLAYER_UUID_LOOKUP |
--[no-]player-uuid-lookup |
1 |
Look up the UUID of a player for the avatar providers, 0 = always use the name, see Player UUIDs |
PLAYER_UUID_PROVIDERS |
--player-uuid-providers |
mojang,minecraftservices,playerdb,minetools |
UUID lookup services in order of preference (fallback) |
PLAYER_UUID_CACHE_TTL |
--player-uuid-cache-ttl |
86400 |
Seconds a UUID is used before it is looked up again (the old one is kept if that fails) |
PLAYER_UUID_CACHE_TYPE |
--player-uuid-cache-type |
memory |
memory (lost on restart) or file |
PLAYER_UUID_CACHE_FILE |
--player-uuid-cache-file |
/tmp/mcstatus-httpd/uuids.json |
JSON file of the UUID cache |
PLAYER_UUID_CACHE_MAX_ENTRIES |
--player-uuid-cache-max-entries |
1000 |
Maximum number of cached UUIDs, 0 = unlimited |
LOGLEVEL |
--loglevel |
INFO |
DEBUG, INFO, WARNING, ERROR, CRITICAL |
At least one server (Java or Bedrock) must be configured.
With HTTPS enabled, HTTP_PORT only serves HTTPS (no plain HTTP), e.g. use HTTP_PORT=8443.
If HTTPS is enabled but the certificate files are missing, the service does not start.
IPv4 + IPv6 behind a reverse proxy (e.g. Docker network with --ipv6):
HTTP_HOST: "::"
HTTP_TRUSTED_PROXIES: "172.18.0.0/16, 10.0.0.5, fd00:dead:beef::/48, ::1"HTTP_TRUSTED_PROXIES accepts any number of IPv4/IPv6 networks and single addresses (comma separated).
Only for requests from these proxies the client IP is taken from X-Forwarded-For/X-Real-IP (shown in the log with LOGLEVEL=DEBUG).
| Without prefix | With HTTP_PREFIX=mcstatus |
Description |
|---|---|---|
/ |
/mcstatus |
Minecraft server status (JSON) |
/healthcheck |
/mcstatus/healthcheck |
Health check of this service (JSON) |
/healthcheck |
Health check, always available without prefix | |
/avatar/<name> |
/mcstatus/avatar/<name> |
Avatar of a Java player who is online (PNG, any name with AVATAR_ONLINE_ONLY=0), optional ?type=body&size=128, only with AVATAR_ENABLE |
GET and HEAD are supported, a trailing slash and query strings (e.g. ?_=123) are ignored (except type and size of the avatar endpoint).
The status endpoint always answers with HTTP 200, an unreachable server is shown with "online": false.
With AVATAR_ENABLE=1 the website examples show avatars instead of the names of Java players (Bedrock provides no player names).
The name is shown as tooltip on mouse over and on tap.
-
The avatars are loaded by mcstatus-httpd and cached, visitors only talk to your server. The avatar providers never see the IP addresses of your visitors.
-
Only players who are currently online are served, other names return 404.
WithAVATAR_ONLINE_ONLY=0any valid Java player name is served, e.g. as profile picture in your forum. Be aware that anybody can then use your service as avatar proxy: random names can push your players out of the cache, delay avatar downloads and cause rate limits at the providers. -
AVATAR_TYPE: default type:head: flat head with helm (e.g. 64x64 px)body: flat front view with the outer skin layer (e.g. 64x128 px)head3d: isometric head without helm, facing right (e.g. 57x60 px, size and perspective differ a little by provider)body3d: isometric body, facing right (e.g. 64x154 px), only provided bymc-heads
All providers deliver the same images for
headandbody.head3dis without helm, becauseminotarandcraftheadhave no 3D head with helm.If no provider delivers a 3D avatar and none is cached (e.g.
mc-headsis down, the only provider ofbody3d), the flat avatar is served instead (head3d→head,body3d→body), so a website shows an avatar and not the name. The replacement is only cached by the browser for 5 minutes, the 3D avatar is tried again after that. The response headerX-Avatar-Typeshows the delivered type. Disable withAVATAR_TYPE_FALLBACK=0. -
AVATAR_IMAGE_SIZE: default image width loaded from the providers ({size}in URL templates). The website examples show the avatars in their own size (AVATAR_SIZEin the HTML) and scale them sharp (pixel art), so 64 is enough for most pages. Own URL templates without{size}always deliver their own size. -
Type and size can be chosen per request, other values return 400:
/avatar/Notch -> AVATAR_TYPE, AVATAR_IMAGE_SIZE /avatar/Notch.png?type=body&size=128 -> body, 128x256 pxAllowed are the values of
AVATAR_ALLOWED_TYPESandAVATAR_ALLOWED_SIZES(a fixed list, so the number of cached variants stays small). -
AVATAR_PROVIDERS: providers in order of preference. If a provider fails (timeout, error, rate limit), the next one is used and the failing one is paused for a while (rate limits: as long as the provider asks for viaRetry-After).- Built-in:
mc-heads(mc-heads.net, all types),minotar(minotar.net) andcrafthead(crafthead.net), both all types exceptbody3d. All are called with the UUID of the player (see Player UUIDs). A provider without the requested type is skipped. - Own URL templates with
{name}(player name) or{id}(UUID, or the name if unknown) and optional{size}, e.g. of a server plugin (must deliver PNG). A template is used for the default type, or with a prefix likehead=orbody3d=for this type:If the plugin uses HTTPS with a self-signed certificate, setAVATAR_PROVIDERS: "head=http://minecraft:8100/tiles/faces/32x32/{name}.png, mc-heads, minotar, crafthead"
AVATAR_TLS_VERIFY: 0(only for own URL templates, preferhttp://inside a Docker network).
- Built-in:
-
Every Java account has a skin (the default skins Steve, Alex etc. if none was chosen). Names without Mojang account (offline-mode servers, Geyser/Floodgate players like
.Name,Anonymous Playerof hidden player lists):mc-headsandcraftheaddeliver the default head (Steve),minotaranswers 404 and the next provider is tried.- Names that are no valid Java names (e.g.
.Name) are not requested at all. - If no provider has an avatar, the website shows the name as text, so every player is listed.
-
Cache:
- Avatars are loaded when they are requested (not on status requests) and used for
AVATAR_CACHE_TTLseconds. After that the next request loads it again from the providers. If none of them delivers it, the old avatar is kept and used (next attempt after 5 minutes). AVATAR_CACHE_TYPE=memory(default): in memory, lost on restart.AVATAR_CACHE_TYPE=file: PNG files inAVATAR_CACHE_DIR(<type>/<size>/<name>.png), kept across restarts. Without a volume the files are kept ondocker restart, but lost when the container is recreated (e.g. image update). To keep them, mount a volume (here together with the UUID cache, see Player UUIDs):A named volume gets the right owner automatically. A bind mount (host directory) must be writable for the user of the container (distroless: UIDenvironment: AVATAR_CACHE_TYPE: file PLAYER_UUID_CACHE_TYPE: file volumes: - mcstatus-cache:/tmp/mcstatus-httpd
65532, alpine:nobody= UID65534). Withread_only: trueuse a volume ortmpfsfor the directory.AVATAR_CACHE_MAX_ENTRIES: the least recently used avatars are removed above the limit (every type and size counts).0= unlimited, recommended only withAVATAR_ONLINE_ONLY=1: with any name allowed, random names (mc-heads delivers the default skin for every name) can fill the cache.
- Avatars are loaded when they are requested (not on status requests) and used for
The avatar providers recommend UUIDs instead of names (names are slower and more likely to be rate limited). mcstatus-httpd looks up the UUID of a player by name when an avatar is loaded (only if a provider uses {id}, the built-in providers do):
PLAYER_UUID_PROVIDERS: lookup services in order of preference. If a service fails (timeout, error, rate limit), the next one is used and the failing one is paused for a while.mojang: api.mojang.comminecraftservices: api.minecraftservices.com (also Mojang, other server)playerdb: playerdb.cominetools: api.minetools.eu
- If a service does not know the name (no Mojang account) or no service answers, the name is used instead, so the avatar providers still get a request.
- UUIDs are cached for
PLAYER_UUID_CACHE_TTLseconds (unknown names at most 1 hour). After that the next avatar download looks it up again, if that fails the old UUID is kept. PLAYER_UUID_CACHE_TYPE=filekeeps the UUIDs across restarts inPLAYER_UUID_CACHE_FILE(JSON), see the volume example above.PLAYER_UUID_LOOKUP=0disables the lookup,{id}is then always the name.- The avatar cache files are named by player name, they do not change.
- examples/index.html: status page for any website (same box code as the wsc-box, incl. the needed CSS), neutral gray design with automatic dark mode
- examples/wsc-box.html: status box for WoltLab Suite (Core) (short: WSC) (ACP → Appearance → Boxes → box type "HTML" *), shows status, players, version, MOTD etc. Remove the lines you do not need.
- examples/wsc-box-mini.html: compact box like "Who is online": status dot, players online / max, the Java player list and the time of the last update. Mouse over shows the status text, address and player names. With only one server its address replaces the server name.
- examples/simple.html: minimal example (plain text, no design, short code for beginners): address, online/offline, players online / max and player names.
The boxes adapt to their own width: in a narrow place (sidebar, phone) Java and Bedrock are shown below each other, in a wide place (footer, own page) side by side.
Icons in examples/icons (own drawing, generated by tools/make-icons.mjs, same license as this project): Minecraft block as SVG and PNG (minecraft-block-icon-<size>.png, 16 to 512 px, e.g. as box icon or favicon) and status icons (status-dot-green.svg, status-dot-red.svg, status-dot-mixed.svg, each also as -plain.svg without the light ring) and plus.svg.
The WSC box in a forum sidebar (with player avatars, the name is shown on mouse over):
Tip: Serve mcstatus-httpd on the same domain as your website via reverse proxy and HTTP_PREFIX (e.g. https://example.com/mcstatus).
A website served via HTTPS cannot load data from a plain http:// URL.
On a different domain this works too (CORS), by default every website is allowed, see HTTP_CORS_ORIGIN to restrict it.
Settings at the top of the script: MCSTATUS_URL, REFRESH_SECONDS, DATE_FORMAT ('' = page/browser language, e.g. de-DE or iso), LANGUAGE, SHOW_AVATARS, AVATAR_TYPE ('' = default of mcstatus-httpd, head, body, head3d or body3d), AVATAR_SIZE (height of a head in px, default 20) and TEXTS.
Languages: the boxes are in English and German. The language is chosen automatically: the language of the page (<html lang>, in WSC the language of the user), on pages without it the language of the browser, otherwise English. LANGUAGE = 'de' sets a fixed language. To add a language, copy the en block of TEXTS in the script (e.g. as es) and translate it.
with docker-compose (see docker-compose.yml):
services:
mcstatus-httpd:
image: ghcr.io/tob1as/mcstatus-httpd:latest
container_name: mcstatus-httpd
restart: unless-stopped
ports:
- 8080:8080/tcp
environment:
#TZ: Europe/Berlin
# Demo servers of https://mcstatus.io - replace with your own servers.
MINECRAFT_JAVA_SERVER: demo.mcstatus.io
MINECRAFT_JAVA_SERVER_PORT: 25565
#MINECRAFT_JAVA_SERVER_PORT_QUERY: 25565 # set, if UDP (query) is not equal to TCP port
#MINECRAFT_JAVA_SERVER_OVERWRITE: minecraft.example.com
MINECRAFT_BEDROCK_SERVER: demo.mcstatus.io
MINECRAFT_BEDROCK_SERVER_PORT: 19132
#MINECRAFT_BEDROCK_SERVER_OVERWRITE: minecraft-bedrock.example.com
#MINECRAFT_BEDROCK_TRANSPORT: auto # nethernet, raknet or auto
#HTTP_PREFIX: mcstatus
#AVATAR_ENABLE: 1 # player avatars (Java), see "Player avatars"
healthcheck:
test: ["CMD", "/venv/bin/python3", "/app/mcstatus-httpd.py", "--healthcheck"]
interval: 60s
timeout: 5s
start_period: 10s
retries: 3The health check uses python3 mcstatus-httpd.py --healthcheck (available since version 2), so no wget/curl is needed (works with the distroless image).
For the alpine image use /usr/local/bin/python3 instead of /venv/bin/python3.
local:
- install:
pip3 install -r requirements.txt - set variables/env or use arguments
- start:
python3 -u ./mcstatus-httpd.py --java-server demo.mcstatus.io --bedrock-server demo.mcstatus.io
docker build --target=production --build-arg VERSION="$(date -u +'%Y%m%d-%H%M%S').$(git rev-parse --short HEAD)" -t ghcr.io/tob1as/mcstatus-httpd:latest -f Dockerfile .The build argument VERSION is shown by --version, /healthcheck and in the logs (env MCSTATUS_HTTPD_VERSION in the image), without it the version is dev.
The GitHub workflow builds the Docker image (ghcr.io/tob1as/mcstatus-httpd):
| Trigger | Version | Image tags |
|---|---|---|
Push of a version tag, e.g. v2.0.0 |
2.0.0 |
2.0.0, 2.0, 2, latest, <build date>.<commit hash>, <commit hash> |
| Manual start in the GitHub GUI or schedule | <build date>.<commit hash> |
dev, <build date>.<commit hash>, <commit hash> |
A version tag additionally creates a GitHub release with mcstatus-httpd-<version>.zip and .tar.gz (script, requirements.txt, README.md, LICENSE and the website examples examples/*.html).
Pre-releases like v2.0.0-rc1 only get the tag 2.0.0-rc1 and are marked as pre-release.
git tag v2.0.0
git push origin v2.0.0-
tests/website: test environment for the website examples (only Docker needed), start with
docker compose -f tests/website/docker-compose.yml up -d --buildand open http://localhost:18092/. Showsindex.html,simple.html,wsc-box.htmlandwsc-box-mini.htmlwith:- live data of the demo servers (mcstatus-httpd built from the current code)
- mock data: random values on every request, or fixed scenarios like many players, Java without Query (player sample only), offline server, only one server, long names and addresses, HTTP error. Own scenarios are simple JSON files.
- an avatar check: all avatar variants of the providers for one player side by side
- a WSC test page: the WSC boxes in the sidebar, in the footer or as own page in the look of a WSC forum (light and dark)
The files in
examples/are used directly, a change is visible after reloading the page. -
tools: helper scripts (Node.js >= 22 and Chrome, Chromium or Edge, no npm packages):
make-icons.mjs: generates the icons ofexamples/icons(SVG, optionally PNG and WebP)screenshot.mjs: screenshot of a page or of a single status box (e.g. for the README), optionally dark mode, mouse over, only one server and anonymized address, players and MOTDwsc-styles.mjs: measures the styles of a WSC page (for the WSC test page)
URL: http://localhost:8080
{
"date": "2026-01-01T12:00:00+00:00",
"java": {
"hostname": "minecraft.example.com",
"port": 25565,
"online": true,
"software": {
"version": "1.21.4",
"brand": "Paper on 1.21.4",
"plugins": ["LuckPerms 5.4.141", "EssentialsX 2.20.1"]
},
"players": {
"online": 3,
"max": 20,
"list": ["Player1", "Player2", "Player3"]
},
"map": "world",
"motd": "A Minecraft Server"
},
"bedrock": {
"hostname": "minecraft-bedrock.example.com",
"port": 19132,
"online": true,
"software": {
"version": "1.21.50",
"brand": "MCPE",
"protocol": 766
},
"players": {
"online": 0,
"max": 20
},
"map": "Bedrock level",
"motd": "Minecraft Bedrock Server",
"gamemode": "Survival"
}
}dateis the time of the query (UTC, ISO 8601). With caching, multiple requests can return the same value.motdis plain text (formatting codes removed).- Java without Query (status fallback):
brandandmaparenull,pluginsis empty andlistonly contains the player sample. - Bedrock via NetherNet:
brandisnull(not provided by NetherNet),motdis the server name.
Unreachable server:
{
"date": "2026-01-01T12:00:00+00:00",
"java": {
"hostname": "minecraft.example.com",
"port": 25565,
"online": false,
"error": "Server unreachable"
}
}Health check (/healthcheck):
{"status": "ok", "timestamp": "2026-01-01T12:00:00+00:00", "version": "20260101120000.a1b2c3d4", "sourcecode": "https://github.com/Tob1as/mcstatus-httpd"}dateis now ISO 8601 in UTC (before: local timeYYYY-MM-DD HH:MM:SS)pluginsandplayers.listare JSON arrays (before: comma separated string)motdis plain text (before: raw text with§formatting codes)- New field
online, unreachable servers no longer break the response /healthcheckreturns JSON (before: emptytext/plain), unknown paths return 404- Bedrock: support for NetherNet (
MINECRAFT_BEDROCK_TRANSPORT),brandisnullvia NetherNet - Command line arguments and new settings (cache, timeout, prefix, CORS, proxies, certificate files)
- HTTPS without certificate files: the service does not start (before: plain HTTP)
- New:
--healthcheck,--version, player avatars (AVATAR_ENABLE) - New: website examples (
examples/, beforeexample.html), icons, test environment and tools
