Here are a few details that might help clarify the situation:
- Ports less than 1024 are (in most OSes) privileged ports that require root to run anything on them. This is intended as a security feature to make it more difficult for an attacker to host services on important ports on a compromised server. As a result, root is required to run anything on port 80 or 443.
- While Nginx and Apache will run on port 80 or 443, as little as possible is done as root. Both immediately spawn a non-root subprocess to work from. The master root process is only used to bind to the privileged ports. Nginx also doesn't run any "apps" itself, but instead communicates with the app running as a completely separate process - hopefully also a low-privilege one.
- You don't want to run your application as root because then if someone finds a RCE vulnerability in it, your system will be very compromised. Of course any RCE vulnerability is bad, but running your application as a low-privilege user is just part of defense in depth, and generally a good idea.
So to spell it out, you don't want to run a custom app directly on port 80/443 because doing so requires you to be root, and you don't want to do that because it's just inviting more trouble in the event of a compromise. However, this is no different than anyone else. While Apache and Nginx will run as root, any application hosted through them typically does not.
This also means that a RCE vulnerability in Apache or Nginx would be very valuable to attackers (if it happens to affect the portion of the application running as root) and, while they do happen, I would say that they are substantially less common than RCE vulnerabilities in your typical web application. That's why it is better to let Apache/Nginx listen on 80/443 instead of your app.