如何使用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
相关产品推荐
相关产品推荐

