(4) 静态前端容器化 - 单页面应用(SAP) History API Fallback - 刷新 404

建议点击 查看原文 查看最新内容。

原文链接: https://typonotes.com/posts/2025/02/09/static-sap-history-api-fallback-issue/

当单页面(SAP)页面使用了路由之后(例如 React-Router), 刷新页面就可能造成 404 问题。 问题主要原因是出在 BrowserRouter, 例如 地址 http://localhost:3000/login

BrowserRouter 依赖于浏览器的 History API 来管理路由。例如,example.com/about 只是一个前端路由,并没有对应的实际 HTML 文件。当你直接在地址栏输入或刷新页面时,服务器会尝试寻找 example.com/about 这个路径的文件,但因为它并不存在,就会返回 404。
  1. 刷新页面直接访问时, 浏览器会直接向后端服务器发送请求。
  2. 后端服务器接收到请求, 发现这个也不是自己提供的服务路径, 就直接返回了 404。

解决方法 需要后端在拿到请求后进行判断。 如果不是 静态资源 例如 js, css 就尝试返回固定内容。

1. nginx 解决方法

在 nginx 中, 可以使用 try_files 捕获这种请求

server {
    listen 80;
    server_name example.com;

    root /var/www/html;  # Vite 构建后的 dist 目录
    index index.html;
    
    # 处理前端静态资源
    location / {
        try_files $uri /index.html;
    }

    # 代理 API 请求到 Gin 服务器
    location /api/ {
        proxy_pass http://localhost:8080;  # Gin 服务器地址
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }

    # Gzip 压缩提升性能(可选)
    gzip on;
    gzip_types text/plain text/css application/json application/javascript;
    gzip_vary on;
}

2. 自定义后端服务器解决方法: gin-gonic/gin

当使用 gin 作为静态服务器的时候, 可以自己创建 中间件(middleware) 处理请求。

  1. 目录模式的情况
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
func main() {
	r := gin.Default()
	r.Static("/", "dist")
}

func HistoryAPIFallback() func(c *gin.Context) {
	return func(c *gin.Context) {

		path := c.Request.URL.Path
		// 如果是 API 请求,或者是静态资源(JS、CSS、图片),则放行
		if strings.HasPrefix(path, "/api/") || strings.Contains(path, ".") {
			c.Next()
			return
		}

		// 返回固定内容。
		c.File("dist/index.html")
		c.Abort()
	}
}