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

将基于App Router的SSR Next.js应用嵌入第三方网站的最优方案

Next.js App Router SSR白标嵌入+主机名识别组织方案

针对你遇到的iframe路由失效、组织识别需求,结合Vercel/Netlify部署环境,给出以下落地解决方案:

一、替换Iframe:解决路由跳转失效问题

iframe的路由隔离特性会导致Next.js客户端路由失效,推荐两种更优的嵌入方案:

1. 微前端嵌入(Module Federation)

将你的应用打包为可共享模块,第三方网站直接引入组件,路由与宿主网站完全兼容:

  • 配置next.config.js启用Module Federation:
    const { NextFederationPlugin } = require('@module-federation/nextjs-mf');
    
    module.exports = {
      webpack(config) {
        config.plugins.push(
          new NextFederationPlugin({
            name: 'whiteLabelApp',
            filename: 'static/chunks/remoteEntry.js',
            exposes: {
              './MainApp': './app/page.js',
              './AuthFlow': './app/auth/page.js',
            },
            shared: {
              react: { singleton: true, requiredVersion: '^18' },
              'react-dom': { singleton: true, requiredVersion: '^18' },
            },
          })
        );
        return config;
      },
    };
    
  • 第三方网站通过远程模块加载你的组件,直接集成到他们的页面路由中,避免iframe的隔离问题。

2. 服务端渲染片段嵌入

第三方网站通过API请求你的应用返回SSR渲染后的HTML片段,直接插入到他们的页面DOM:

  • 在App Router中创建API路由,接收主机名参数,返回对应组织的渲染内容:
    // app/api/embed/route.ts
    import { renderToString } from 'react-dom/server';
    import MainApp from '../../page';
    import { getOrgData } from '@/lib/data';
    
    export async function GET(request: Request) {
      const { searchParams } = new URL(request.url);
      const host = searchParams.get('host');
      const orgId = getOrgIdByHost(host); // 此处复用后面的主机名匹配逻辑
      const orgData = await getOrgData(orgId);
      
      const html = renderToString(<MainApp orgData={orgData} />);
      return new Response(html, { headers: { 'Content-Type': 'text/html' } });
    }
    
  • 第三方网站通过AJAX请求该接口,将HTML插入到页面指定位置,完全规避iframe问题。

二、基于主机名识别组织(适配App Router SSR)

利用Next.js中间件拦截请求,解析主机名匹配组织ID,传递给页面组件:

1. 中间件映射主机名到组织ID

创建middleware.ts全局拦截请求,完成主机名-组织ID的映射:

import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

// 从环境变量加载主机名映射,生产环境建议从数据库动态获取
const ORG_DOMAIN_MAP = JSON.parse(process.env.ORG_DOMAIN_MAP || '{}');

export function middleware(request: NextRequest) {
  const host = request.headers.get('host')?.replace(/:\d+$/, '');
  if (!host) return NextResponse.next();

  const orgId = ORG_DOMAIN_MAP[host];
  if (!orgId) return new NextResponse('Unauthorized', { status: 403 });

  // 将组织ID注入请求头,供页面组件获取
  const response = NextResponse.next();
  response.headers.set('X-Org-ID', orgId);
  return response;
}

export const config = { matcher: '/:path*' };

2. 页面组件获取组织ID并查询数据

在App Router的页面组件中,通过headers()获取组织ID,查询对应数据:

// app/page.tsx
import { headers } from 'next/headers';
import { getOrgData } from '@/lib/data';

export default async function Home() {
  const headersList = headers();
  const orgId = headersList.get('X-Org-ID');
  if (!orgId) throw new Error('Organization not found');

  const orgData = await getOrgData(orgId);

  return (
    <div className="container">
      <h1>{orgData.name}</h1>
      {/* 渲染组织专属内容 */}
    </div>
  );
}

三、Vercel/Netlify部署配置

Vercel部署

  • 自定义域名:在项目Settings > Domains中添加第三方域名,指导对方将DNS记录CNAME到你的Vercel项目域名。
  • 环境变量:在Settings > Environment Variables中添加ORG_DOMAIN_MAP,值为JSON字符串(如{"client1.com":"org_123","client2.net":"org_456"})。
  • Vercel原生支持Next.js App Router和中间件,无需额外配置。

Netlify部署

  • 自定义域名:在Domain settings中添加第三方域名,完成DNS解析配置。
  • 环境变量:在Site settings > Environment variables中添加ORG_DOMAIN_MAP。
  • Netlify自动兼容Next.js中间件,确保next.config.js配置正确即可。

四、Iframe临时兼容方案(迫不得已时使用)

如果必须保留iframe,可修改跳转逻辑解决路由失效问题:

// 自定义跳转工具函数
export const externalNavigate = (path: string) => {
  if (window.top !== window.self) {
    // 在iframe中时,直接操作父页面路由
    window.top.location.href = `${window.top.location.origin}${path}`;
  } else {
    window.location.href = path;
  }
};
  • 使用该函数替代Next.js的useRouter跳转,但体验远不如前两种方案,仅作临时过渡。

内容的提问来源于stack exchange,提问作者tamim abweini

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 19:41:28