You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用React和Nginx实现动态子域名及多场景内容分发

实现React + Nginx动态子域名多应用方案

一、核心逻辑

将主域名foo.com与所有子域名请求统一指向同一个React+后端项目,通过解析请求域名的子域名部分,在后端校验用户合法性后,向前端返回对应渲染标识:主域名渲染管理后台(组件A),合法子域名渲染用户仪表盘(组件B),不存在的子域名直接返回404。

二、后端配置(以Node.js/Express为例)

1. 子域名解析中间件

提取请求头中的域名,拆分出子域名并挂载到请求对象:

app.use((req, res, next) => {
  const host = req.get('host');
  const domainParts = host.split('.');
  let subdomain = '';

  // 生产环境适配foo.com域名格式
  if (domainParts.length >= 2 && domainParts.at(-2) === 'foo') {
    subdomain = domainParts[0];
  }
  // 本地开发适配bar.localhost:3000格式
  else if (domainParts.length === 3 && domainParts[1] === 'localhost') {
    subdomain = domainParts[0];
  }

  req.subdomain = subdomain;
  next();
});

2. 用户校验与数据接口

模拟用户数据库,校验子域名对应用户是否存在,返回对应标识或404:

// 模拟用户存储(实际用数据库替代)
const validUsers = new Set(['bar', 'baz']);

// 获取当前访问的应用类型与用户数据
app.get('/api/app-config', (req, res) => {
  const { subdomain } = req;

  // 主域名(无有效子域名)返回管理后台标识
  if (!subdomain || subdomain === 'www') {
    return res.json({ type: 'admin' });
  }

  // 子域名用户不存在,返回404
  if (!validUsers.has(subdomain)) {
    return res.status(404).json({ error: 'User not found' });
  }

  // 返回用户专属数据
  return res.json({
    type: 'user',
    username: subdomain,
    dashboardData: {
      totalOrders: 128,
      recentActivity: ['2024-05-01', '2024-05-02']
    }
  });
});

// 管理后台添加用户接口
app.post('/api/add-user', express.json(), (req, res) => {
  const { username } = req.body;
  if (!username || validUsers.has(username)) {
    return res.status(400).json({ error: 'Invalid or duplicate username' });
  }
  validUsers.add(username);
  res.json({ success: true, username });
});

3. React静态文件与路由 fallback

将所有非API请求转发到React的index.html,确保前端路由生效:

const path = require('path');
app.use(express.static(path.join(__dirname, 'react-build')));

app.get('*', (req, res) => {
  res.sendFile(path.join(__dirname, 'react-build', 'index.html'));
});

三、React前端实现

1. 根组件分发逻辑

在应用初始化时请求后端接口,根据返回结果渲染对应组件:

import { useState, useEffect } from 'react';
import AdminPanel from './components/AdminPanel';
import UserDashboard from './components/UserDashboard';
import NotFound from './components/NotFound';

function App() {
  const [appState, setAppState] = useState({
    type: null,
    userData: null,
    isNotFound: false,
    loading: true
  });

  useEffect(() => {
    fetch('/api/app-config')
      .then(res => {
        if (res.status === 404) {
          setAppState(prev => ({ ...prev, isNotFound: true, loading: false }));
          return;
        }
        return res.json();
      })
      .then(data => {
        if (data) {
          setAppState({
            type: data.type,
            userData: data.type === 'user' ? data : null,
            isNotFound: false,
            loading: false
          });
        }
      })
      .catch(() => {
        setAppState(prev => ({ ...prev, isNotFound: true, loading: false }));
      });
  }, []);

  if (appState.loading) return <div>Loading...</div>;
  if (appState.isNotFound) return <NotFound />;

  return (
    <div className="app-container">
      {appState.type === 'admin' && <AdminPanel />}
      {appState.type === 'user' && <UserDashboard data={appState.userData} />}
    </div>
  );
}

export default App;

2. 管理后台组件(组件A)

实现用户添加功能:

import { useState } from 'react';

function AdminPanel() {
  const [username, setUsername] = useState('');
  const [msg, setMsg] = useState('');

  const handleAddUser = async (e) => {
    e.preventDefault();
    setMsg('');
    try {
      const res = await fetch('/api/add-user', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ username })
      });
      const data = await res.json();
      if (res.ok) setMsg(`用户 ${data.username} 添加成功`);
      else setMsg(data.error);
    } catch (err) {
      setMsg('添加失败,请重试');
    }
    setUsername('');
  };

  return (
    <div className="admin-panel">
      <h1>用户管理后台</h1>
      <form onSubmit={handleAddUser}>
        <input
          type="text"
          value={username}
          onChange={(e) => setUsername(e.target.value)}
          placeholder="输入用户名(将作为子域名)"
          required
        />
        <button type="submit">添加用户</button>
      </form>
      {msg && <p className="msg">{msg}</p>}
    </div>
  );
}

export default AdminPanel;

四、Nginx反向代理配置

将所有域名请求转发到后端服务,同时处理静态文件:

server {
    listen 80;
    server_name foo.com *.foo.com;

    # React静态文件根目录
    root /var/www/react-app/build;
    index index.html;

    # API请求转发到后端
    location /api {
        proxy_pass http://localhost:3001; # 后端服务端口
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }

    # 前端路由 fallback
    location / {
        try_files $uri $uri/ /index.html;
    }

    # 后端返回404时,渲染React的404页面
    error_page 404 /index.html;
}

五、关键注意事项

  • DNS配置:在域名服务商处设置泛域名解析*.foo.com,指向服务器公网IP。
  • 本地测试:修改系统hosts文件,添加127.0.0.1 foo.com bar.foo.com模拟域名访问。
  • HTTPS适配:申请泛域名SSL证书,在Nginx中配置listen 443 ssl并指定证书路径。
  • Cookie共享:若需主域名与子域名共享登录状态,设置Cookie的domain为.foo.com(注意前缀点)。

内容的提问来源于stack exchange,提问作者4bs3nt

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 17:40:58