Why forwarding needs a complete security design
Velocity authenticates the public player connection. Paper runs with online-mode=false so it accepts the identity forwarded by the proxy. That backend setting is dangerous by itself: if a player can connect directly, they may be able to impersonate another account.
Modern forwarding protects the forwarded identity with a shared secret. PaperMC still recommends a firewall or equivalent network isolation because the secret is a second layer, not a replacement for blocking the backend.
Start with the Velocity Docker Compose setup if proxy and backend are not running yet.
Required state on the proxy
Stop the stack before editing configuration:
docker compose down
In proxy/velocity.toml, configure:
online-mode = true
player-info-forwarding-mode = "modern"
forwarding-secret-file = "forwarding.secret"
Velocity's online-mode=true keeps Mojang/Microsoft account authentication at the edge. The modern format is intended for Minecraft 1.13 and newer and is supported directly by current Paper releases.
Read the value from proxy/forwarding.secret without putting it into a command line, Git commit, screenshot, or support message. If the file does not exist, start Velocity once, let it generate the configuration, and stop it again.
Required state on Paper
The backend must already have started once so lobby/config/paper-global.yml exists.
First, set this in lobby/server.properties:
online-mode=false
Then edit lobby/config/paper-global.yml:
proxies:
velocity:
enabled: true
online-mode: true
secret: "replace-with-the-exact-forwarding-secret"
The Paper online-mode value under proxies.velocity must match the proxy's online-mode. It does not replace server.properties; the two fields describe different sides of the forwarded authentication flow.
If you previously used legacy BungeeCord forwarding, disable it in spigot.yml:
settings:
bungeecord: false
Do not enable modern and legacy forwarding together. Velocity supports one forwarding mode for the connection path.
Keep the backend private
For backends in the same Compose project, omit ports entirely:
services:
proxy:
ports:
- "25565:25577"
networks: [minecraft]
lobby:
networks: [minecraft]
networks:
minecraft: {}
Velocity reaches lobby:25565 inside Docker. The Paper service does not need a host port. Verify the resolved configuration instead of trusting indentation:
docker compose config
docker compose port proxy 25577
docker compose port lobby 25565
The first command should show the proxy mapping. The last command should show no published backend mapping.
If proxy and backend live on different physical hosts, a shared Compose network does not exist. Restrict the Paper port at the host/provider firewall to the proxy source address or carry the traffic through a private encrypted network. Never expose an offline-mode backend to the general internet.
Restart and verify identity forwarding
Start the network and watch both log streams:
docker compose up -d
docker compose logs -f proxy lobby
Join through the public proxy address. A successful test has all of these properties:
- Velocity authenticates the Java account.
- Paper accepts the connection without a forwarding error.
- The backend sees the player's real UUID, skin, and address information supplied by Velocity.
- A direct connection to the backend host port is impossible.
- Restarting both containers does not change the secret or disable forwarding.
Test with a non-operator account before considering the network ready. An operator login can hide permission and routing mistakes.
Rotate an exposed secret
If the secret appeared in Git, logs, chat, or a screenshot, assume it is compromised:
- Stop Velocity and every backend.
- Replace
proxy/forwarding.secretwith a new long random value. - Put the same value into every backend's Paper configuration.
- Confirm backend ports are still private.
- Restart the entire network and test a real login.
Changing only one side causes intentional login failures. Rotate as one controlled operation.
Troubleshooting
| Message or symptom | Cause | Fix |
|---|---|---|
This server requires you to connect with Velocity | Paper expects modern forwarding but Velocity does not send it | Set proxy mode to modern and restart both sides |
| Login fails after secret rotation | Proxy and Paper secrets differ | Copy the exact new value to every backend |
| UUIDs or skins are wrong | Legacy/no forwarding is still active | Disable Bungee forwarding and verify the modern configuration |
| Direct backend login succeeds | Host port or firewall still exposes Paper | Remove mapping and close external access immediately |
| Proxy cannot reach Paper at all | Docker service/network/port mismatch | Use the backend connection runbook |
Next steps
- Diagnose routing with Velocity backend connection checks.
- Add crossplay using Geyser and Floodgate on the proxy.
- Apply the broader Docker Minecraft hardening baseline.