React + FastAPI 前后端分离架构下的 Nginx 配置实践

React + FastAPI 前后端分离架构下的 Nginx 配置实践

前言

在现代 Web 开发中,前后端分离已经成为主流架构。前端使用 React 等框架构建单页应用(SPA),打包成静态资源;后端使用 FastAPI 等框架提供 RESTful API。两者通过 HTTP 接口通信,各自独立开发、独立部署。

在这套架构中,Nginx 扮演着至关重要的角色:它既是前端静态资源的托管服务器,又是后端 API 的反向代理,同时还承担着 HTTPS 终结、压缩、缓存、安全防护等职责。本文将以 React + FastAPI 为例,系统讲解一份生产可用的 Nginx 配置,并重点剖析前端静态资源托管中容易被忽视的细节。

一、整体架构

我们假设一个典型的部署结构:

  • React 前端 :执行 npm run build(或 vite build)后,生成的静态文件位于 /var/www/frontend/dist
  • FastAPI 后端 :通过 Uvicorn 或 Gunicorn 运行在本地的 127.0.0.1:8000
  • Nginx :作为对外唯一入口,监听 80/443 端口,将 /api/ 开头的请求转发给 FastAPI,其余请求交由 React 静态资源处理。

这种设计的好处在于前后端同域访问,天然规避了跨域问题,同时对外只暴露一个入口,安全性和可维护性都更好。

二、完整配置概览

先来看整体配置骨架,后文会逐块展开:

nginx 复制代码
upstream fastapi_backend {
    server 127.0.0.1:8000;
    keepalive 32;
}

# HTTP 自动跳转 HTTPS
server {
    listen 80;
    server_name example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    http2 on;
    server_name example.com;

    ssl_certificate     /etc/nginx/ssl/example.com.crt;
    ssl_certificate_key /etc/nginx/ssl/example.com.key;
    ssl_protocols       TLSv1.2 TLSv1.3;

    root  /var/www/frontend/dist;
    index index.html;
    client_max_body_size 20m;

    # ...(静态资源、API 代理、SPA 回退等,见下文)
}

三、React 前端静态资源托管

前端部分是本文的重点。React 静态资源的托管看似只是「把文件发出去」,但缓存策略、SPA 路由、压缩、安全头这几处细节处理不当,就会导致白屏、更新不生效、加载失败难以排查等线上问题。

3.1 分层缓存策略

React 前端的缓存遵循一个核心原则:入口文件不缓存,带 hash 的资源永久缓存

Vite/Webpack 打包时会给 JS/CSS 文件名加上内容 hash(如 index-a1b2c3d4.js)。文件内容一旦改变,hash 就会改变,文件名也随之改变。因此这些文件可以放心设置一年的 immutable 缓存------因为内容变了就是新文件名,浏览器必然会重新请求。

index.html 就像一张「地图」,记录着当前版本引用了哪些 hash 文件。它本身必须每次都校验,否则用户会拿着旧的 index.html 去请求早已不存在的旧 hash 文件,从而导致白屏。

不同资源的缓存策略可以归纳如下:

文件类型 缓存策略 原因
index.html 不缓存 引用了带 hash 的资源,必须实时更新
/assets/*.[hash].js 一年 + immutable 内容变则文件名变,绝对安全
图片 / 字体等 30 天 更新不频繁,无 hash 保障时留余量
service-worker.js 不缓存 缓存会导致 PWA 无法更新

对应的配置如下:

nginx 复制代码
# index.html ------ 绝不缓存
location = /index.html {
    add_header Cache-Control "no-cache, no-store, must-revalidate";
    expires -1;
}

# 带 hash 的构建产物 ------ 永久缓存
location /assets/ {
    expires 1y;
    add_header Cache-Control "public, immutable";
    access_log off;
}

# 其他静态资源 ------ 中期缓存
location ~* \.(?:js|css|png|jpg|jpeg|gif|ico|svg|webp|woff2?|ttf|eot)$ {
    expires 30d;
    add_header Cache-Control "public";
    access_log off;
    try_files $uri =404;
}

这里 Cache-Control: public, immutable 中的 immutable 值得特别说明:它告诉浏览器这个资源在有效期内绝不会变。这样即使用户主动点击刷新,浏览器也不会发起协商请求(If-Modified-Since)去询问服务器,而是直接使用本地缓存,进一步减少了请求数量。

另外注意静态资源正则块中加了 try_files $uri =404。如果不加,请求一个不存在的静态文件时会走到后面的 SPA 回退逻辑,返回 index.html(HTTP 200),从而掩盖资源加载失败的真实问题。加上 =404 能让缺失资源正确返回 404,便于线上排查。

3.2 SPA 路由回退

这是前后端分离中最容易被忽视、也最容易出问题的一点:

nginx 复制代码
location / {
    try_files $uri $uri/ /index.html;
}

React Router 在使用 HTML5 History 模式时,像 /user/profile 这样的路径在浏览器地址栏是合法的,但一旦用户直接访问或刷新该页面,浏览器会向服务器请求这个「文件」,而它在磁盘上并不存在,于是返回 404。

try_files $uri $uri/ /index.html 的逻辑是:先查找真实文件,找不到就返回 index.html,把路由控制权交还给前端的 React Router 处理。

需要强调的是,这条规则必须放在所有 location 的最后 。因为 Nginx 的 location 匹配有优先级,精确匹配(=)和正则匹配都会优先于这个通用的 /。如果放在前面,可能会误拦截本该走静态资源或 API 的请求。

3.3 压缩

开启 Gzip 压缩能显著减小文本类资源(JS/CSS/JSON)的传输体积:

nginx 复制代码
gzip              on;
gzip_vary         on;
gzip_min_length   1024;
gzip_comp_level   6;
gzip_types        text/plain text/css application/json
                  application/javascript image/svg+xml
                  application/wasm;
# 若已编译 ngx_brotli 模块,可进一步开启:
# brotli        on;
# brotli_static on;

如果构建流程中已经生成了预压缩的 .br 文件,配合 brotli_static on 可以直接返回预压缩产物,压缩率比 Gzip 更高,且无需运行时消耗 CPU。

3.4 安全响应头

nginx 复制代码
add_header X-Frame-Options        "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy        "strict-origin-when-cross-origin" always;

这里的 always 参数确保即使在返回 4xx/5xx 状态码时也带上这些安全头------不加 always 时,Nginx 默认只在 2xx/3xx 响应中添加头部。

需要特别注意 add_header 的「继承覆盖」特性:如果某个 location 块内部又写了自己的 add_header,它会覆盖掉 server 块中的所有 add_header,而不是叠加。因此若某个 location 需要额外的头部,往往需要把 server 级别的头部在该 location 内重新声明一遍。

四、FastAPI 后端代理(简述)

后端部分相对简单,Nginx 只需把 /api/ 前缀的请求转发给 FastAPI:

nginx 复制代码
location /api/ {
    proxy_pass http://fastapi_backend;
    proxy_set_header Host            $host;
    proxy_set_header X-Real-IP       $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_http_version 1.1;
    proxy_set_header Connection "";
}

需要注意 proxy_pass http://fastapi_backend; 末尾不带斜杠 ,会保留 /api/ 前缀转发给后端,此时 FastAPI 的路由也应以 /api/ 开头。若希望剥离前缀,可在末尾加斜杠写成 proxy_pass http://fastapi_backend/;

后端启动时只需信任 Nginx 传来的代理头即可正确识别 HTTPS 和真实客户端 IP:

bash 复制代码
uvicorn main:app --host 127.0.0.1 --port 8000 --proxy-headers

由于前后端同域访问,通常无需在 FastAPI 中配置 CORS ;只有当前端与 API 使用不同域名时才需要引入 CORSMiddleware

五、小结

一份看似简单的 Nginx 配置,背后藏着不少工程经验。回顾本文,React 前端托管有几个关键要点值得记牢:入口文件 index.html 绝不缓存、带 hash 的资源永久缓存,是保证「发版即生效」的基础;SPA 路由回退规则要放在最后,且警惕它掩盖资源 404 的副作用;安全头记得加 always 并注意 add_header 的覆盖特性。

而后端代理的核心,则是理解 proxy_pass 尾部斜杠对路径的影响,以及通过代理头把真实请求信息透传给 FastAPI。

相关推荐
LaughingZhu3 小时前
Product Hunt 每日热榜 | 2026-07-15
前端·人工智能·神经网络·react.js·搜索引擎·前端框架
2601_956743683 小时前
上海AI Agent智能体开发选型:从工程架构看落地路径解析
大数据·人工智能·ai·架构·开发经验·上海
用户239526180103 小时前
一文读懂LangChain模型:AI应用的“大脑”是如何工作的
架构
光影少年4 小时前
RN中的StyleSheet.create 好处、内联样式弊端
react native·react.js·掘金·金石计划
我是大卫4 小时前
【图】React源码解析-第六部分:高级算法与执行链路
react.js·源码
喜欢踢足球的老罗4 小时前
企业微信代开发应用组织架构同步踩坑实录
架构·企业微信·代开发应用
胡萝卜术5 小时前
V040:RAG 检索增强生成的工程链路解构与生产级系统设计
面试·架构·github
艾德克斯11 小时前
AI服务器电源POWER SHELF测试破局:HVDC高压架构挑战与ITECH全栈测试方案
人工智能·架构
山东云弈创峰科技15 小时前
山东云弈创峰:跨境电商AI自动化中的环境隔离与风控架构
人工智能·架构·自动化