How do I forward a port to a subdomain for non-http services

Solution 1:

Is there a way for me to, say, open up just one port, like 25565, and allow users to connect to my other minecraft servers, let's say hosted on 25566 and 25567 with subdomains?

This generally depends on whether the protocol allows clients to specify which domain they want (such as the "Host:" header in HTTP or the "Server Name Indication" in TLS).

For Minecraft Java Edition, it seems that this is possible as the handshake packet has a "host" field, and I found a Minecraft reverse proxy called "BungeeCord" which has the forced_hosts option to map domains to specific servers.

Many other protocols don't have anything like that.

I don't want to keep opening up ports as it is unsafe.

No, that is nonsense.

Ports themselves don't do anything – the actual services do. If you were running the exact same service on 10 ports, you wouldn't be 10x more vulnerable – it is still the same service and the ports do not change how it behaves. Likewise, if you were running three different services accessible through one port, it would still be the same security risk as running those services individually on separate ports.

This might not be entirely true if they were in fact HTTP services, and you used NGINX as an HTTP reverse proxy which could itself handle the requests and therefore shield poorly written services from bugs in their HTTP parsers (but not from bugs in the service logic itself!). But with Minecraft-over-NGINX that is not the case, as NGINX doesn't understand anything about the Minecraft protocol and therefore cannot meaningfully increase security. (For the same reason, NGINX can't be used to do virtual hosts/subdomains with Minecraft, as that too would require understanding the protocol at least a little bit.)

On the other hand, with a proxy like BungeeCord, you still have to worry about bugs in your Minecraft server and bugs in the proxy itself (as it does process commands).