Билд SPA приложения без SSR, в нашем случае это Vite.js + React, представляет собой набор статических файлов, с отдачей которых справится базовая настройка Nginx с минимальными изменениями.
В нашем случае мы используем Webmin/Virtualmin для настройки, но это не особо влияет на саму конфигурацию.
Для запуска SPA приложения на базовой конфигурации Nginx сервера, нам не хватает нескольких моментов:
- Переадресация всех путей на наш рут, /index.html в данном случае;
- Зарезервированный путь для получения/обновления SSL сертификатов.
Переадресация путей
Тут нам надо исправить 2 момента:
#Установим root и точку входа, у нас index.html в папка dist
root /home/my_domain/my_domain/dist/; 
index index.html;# Укажем, как обрабатывать пути, кроме статических файлов (отбор по расширениям)
location / {
        if (-f $request_filename) {
            	expires max;
            	break;
        }
        if ($request_filename !~ "\.(js|htc|ico|gif|jpg|png|css)$") {
            	rewrite ^(.*) /index.html last;
        }
}
Получение/обновление SSL
В данном случае мы используем Let’s Encrypt, Webmin для автоматической работы с этими сертификатами использует папку /.well-known, соответственно выделяем ее так, что бы она не обрабатывалась нашим SPA — например меняем для этого location root.
location /.well-known/ {
	root /home/my_domain/public_html;
}Итоговый шаблон
server {
	server_name my_domain.ru www.my_domain.ru;
	# your server IP
	listen 00.00.00.00;
	root /home/my_domain/my_domain/dist/;
	index index.html;
	access_log /var/log/virtualmin/my_domain.ru_access_log;
	error_log /var/log/virtualmin/my_domain.ru_error_log;
	fastcgi_param GATEWAY_INTERFACE CGI/1.1;
	fastcgi_param SERVER_SOFTWARE nginx;
	fastcgi_param QUERY_STRING $query_string;
	fastcgi_param REQUEST_METHOD $request_method;
	fastcgi_param CONTENT_TYPE $content_type;
	fastcgi_param CONTENT_LENGTH $content_length;
	fastcgi_param SCRIPT_FILENAME /home/my_domain/public_html$fastcgi_script_name;
	fastcgi_param SCRIPT_NAME $fastcgi_script_name;
	fastcgi_param REQUEST_URI $request_uri;
	fastcgi_param DOCUMENT_URI $document_uri;
	fastcgi_param DOCUMENT_ROOT /home/my_domain/public_html;
	fastcgi_param SERVER_PROTOCOL $server_protocol;
	fastcgi_param REMOTE_ADDR $remote_addr;
	fastcgi_param REMOTE_PORT $remote_port;
	fastcgi_param SERVER_ADDR $server_addr;
	fastcgi_param SERVER_PORT $server_port;
	fastcgi_param SERVER_NAME $server_name;
	fastcgi_param PATH_INFO $fastcgi_path_info;
	fastcgi_param HTTPS $https;
	location /.well-known/ {
		root /home/my_domain/public_html;
	}
	location / {
        	if (-f $request_filename) {
            		expires max;
            		break;
        	}
        	if ($request_filename !~ "\.(js|ico|gif|jpg|png|css)$") {
            		rewrite ^(.*) /index.html last;
        	}
    	}
	# your server IP
	listen 00.00.00.00:443 ssl;
	ssl_certificate /etc/ssl/virtualmin/PATH_TO_KEY/ssl.combined;
	ssl_certificate_key /etc/ssl/virtualmin/PATH_TO_KEY/ssl.key;
}
Итого
Очевидно, что все папки и адреса Вам необходимо заменить на собственные, но идея остается прежней: перехватываем все пути и замыкаем на index.html, а отдельные служебные пути выделяем в location и оставляем например в стандартной public_html для удобства.
Так же при необходимости использования API с этого же сервера, Вы можете самостоятельно добавить location по типу /api/ и сделать редирект на обработчик например.