Skip to content

Security

Read this before you open the API to the internet.

HMTAPI has no API keys, no login, no tokens and no rate limiting. Anyone who can reach the port can read every endpoint you have configured — including player names, balances, ranks and anything else your PlaceholderAPI placeholders return.

On its own, HMTAPI is designed to sit behind a proxy or a firewall, not to face the internet directly.

What an endpoint returns is up to you and the plugins you use. An endpoint is only as sensitive as its values:

Value Exposure
{username}, {last_login}, {last_seen} Player identity and activity
{papi:%vault_eco_balance%} Financial data
{papi:%luckperms_prefix%} Staff and rank information
{papi:%server_online%} Harmless

If you would not publish it on a public Discord channel, do not put it on an endpoint that is reachable from the internet.

If a reverse proxy runs on the same machine, bind the API to the loopback interface so nothing outside can reach it directly:

bind_address: "127.0.0.1"

bind_address needs a server restart to change.

If the proxy is on another machine in the same network, bind to that machine’s private address instead of 0.0.0.0:

bind_address: "192.168.1.20"

If HMTAPI must bind to all interfaces, restrict the port at the firewall so only your proxy can reach it. On a typical Linux server with ufw:

Terminal window
# Allow the API only from the local machine
sudo ufw allow from 127.0.0.1 to any port 4567
# Or only from your reverse proxy's IP
sudo ufw allow from 10.0.0.5 to any port 4567

A reverse proxy is the recommended setup. It lets you add HTTPS, authentication, access control, caching and request logging — none of which HMTAPI provides itself.

A minimal NGINX example with a basic-auth prompt in front of a localhost-bound HMTAPI:

server {
listen 443 ssl;
server_name api.example.com;
ssl_certificate /etc/letsencrypt/live/api.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.example.com/privkey.pem;
location / {
auth_basic "HMTAPI";
auth_basic_user_file /etc/nginx/.htpasswd;
proxy_pass http://127.0.0.1:4567;
proxy_set_header Host $host;
}
}

With bind_address: "127.0.0.1" in config.yml, the API is only reachable through the proxy.

A Caddy example with automatic HTTPS:

api.example.com {
basicauth {
user $2a$14$...
}
reverse_proxy 127.0.0.1:4567
}
  • Prefer server-wide endpoints for public dashboards. require_player: false with %server_online%-style placeholders exposes nothing personal.
  • Do not add an endpoint per player. One player endpoint with a name in the URL already covers every player.
  • Leave out staff data. Rank and prefix placeholders from permissions plugins reveal who has permissions on your server.
  • Review your config before exposing it. Read your own object sections and ask whether each value is something you want to be public.

The default NGINX configuration logs every request path, which includes the player names you request. If player names are sensitive to you, disable or restrict access to those logs.

  • Bind to 127.0.0.1 or a private address
  • Firewall the port so only the proxy can reach it
  • Put a reverse proxy with HTTPS in front
  • Require authentication on the proxy
  • Reviewed every endpoint for sensitive values
  • Decided what to do with request logs containing player names
  • Tested from outside your network