Build the frontend and publish the generated dist directory as the Nginx document root:
npm run build
The admin web app uses Vue Router HTML5 history mode. The active Nginx server block must therefore contain:
location / {
try_files $uri $uri/ /index.html;
}
Use nginx.conf as the site template. If the host already terminates HTTPS in another server block, merge the location blocks into that HTTPS block instead of creating a second TLS listener. Keep /api/ and /uploads/ proxied to the Spring Boot service; only client-side page routes should fall back to index.html.
The backend maps uploaded files as follows:
/uploads/avatars/** -> AVATAR_STORAGE_DIR (default: ./data/avatars)
/uploads/guide-applications/** -> GUIDE_APPLICATION_STORAGE_DIR (default: ./data/guide-applications)
The recommended Nginx configuration is already included in nginx.conf:
location ^~ /uploads/ {
proxy_pass http://127.0.0.1:8011;
}
This preserves the backend authorization check for guide application documents. If avatars must be served directly by Nginx, only expose the avatar directory and keep guide applications proxied:
location ^~ /uploads/avatars/ {
alias /opt/tourguide/data/avatars/;
autoindex off;
}
location ^~ /uploads/guide-applications/ {
proxy_pass http://127.0.0.1:8011;
}
Replace /opt/tourguide/data/... with the actual absolute server paths. Configure the same absolute paths through AVATAR_STORAGE_DIR and GUIDE_APPLICATION_STORAGE_DIR for the backend, so storage does not depend on the Java process working directory.
After reloading Nginx, verify all three request types:
GET / -> frontend index.html (200)
GET /login -> frontend index.html (200)
GET /api/health -> backend JSON (200)