Существует три варианта решения этой проблемы:
- После сборки фронтенда можно помещать его в static files / assets бэкенда, и сервить так же, как и все остальные файлы. Этот метод плох тем, что утрачивается возможность применения cache forever / cache busting (добавления хэш суммы в имена файлов).
- Распределять запросы между сервисами используя договоренность о путях, например, /api/* и /admin/* отправлять в бэк, /static/* отдавать из каталога с VueJS статикой, а для всех остальных запросов возвращать dist/index.html (для избавления от # в путях и обеспечения поддержки history mode). Минус - об этой договоренности нужно помнить, и следить за её соблюдением. Появляется неявная зависимость между сервисами.
- Использовать разные поддомены, например, api.example.com для бэка, www.example.com - для фронта. Роутинг запросов в этом случае происходит наиболее естественным образом - на основании имени домена. Становятся доступны дополнительные способы масштабирования (размещение на разных серверах, dns round robin, использование разных reverse proxy), и появляется возможность тестирования / отладки фронта с апи из разных окружений (dev, staging, qa, prod).
ps. Cоветую упаковать ваши сервисы в Docker контейнеры, а в качестве reverse proxy / tls proxy / load balancer использовать Traefik вместо Nginx. Его киллер-фичи - auto discovery и auto load balancing. Кроме того, его гораздо проще правильно настроить. Сам Traefik тоже в контейнере работает, поэтому на сервере не требуется ничего кроме docker и docker-compose