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

Angular16+Ionic+PM2部署后内部页SSR无日志,需动态设置OG元标签

问题解决与代码优化

问题分析

  1. 生产环境内部页面无日志:大概率是生产环境的反向代理(如Nginx)未将内部页面请求转发至PM2运行的Node服务,直接返回静态index.html;或是server.ts中静态资源路由与页面路由的匹配逻辑存在冲突,导致内部页面请求未触发SSR路由的日志输出。
  2. 内部页面OG元标签动态设置:当前通过checkAndAddTags修改HTML字符串的方式缺乏Angular SSR原生元数据管理能力,需优化逻辑确保不同页面生成对应OG标签。

解决方案与代码修改

1. 修复生产环境日志触发问题

第一步:检查Nginx配置(若使用Nginx)

确保所有页面请求都转发至PM2服务,示例配置:

server {
    listen 80;
    server_name your-domain.com;

    root /usr/share/nginx/html/dist/monapp/browser;
    index index.html;

    # 静态文件直接返回
    location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
    }

    # 所有页面请求转发至Node服务(PM2监听端口,示例为3000)
    location / {
        try_files $uri $uri/ @node;
    }

    location @node {
        proxy_pass http://localhost:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

第二步:修正server.ts路由与路径配置

调整静态资源路径和视图目录,避免路由冲突,确保所有页面请求触发SSR日志:

const proxyConfig = environment.production ?
  require('./proxy.conf-prod.json') :
  require('./proxy.conf-dev.json');
console.log(proxyConfig)

export function app(): express.Express {
  const app = express();
  const distFolder = join(process.cwd(), 'dist/monapp/browser');
  const index = existsSync(join(distFolder, 'index.original.html')) ? 'index.original.html' : 'index';

  app.engine('html', ngExpressEngine({
    bootstrap: AppServerModule,
  }));

  // 统一静态资源路径,避免生产环境路径冲突
  app.use('/assets', express.static(join(distFolder, 'assets'), {
    maxAge: '1y'
  }));

  // 统一视图目录,修复生产环境绝对路径渲染问题
  const viewsFolder = environment.production ? '/usr/share/nginx/html/dist/monapp/browser' : distFolder;
  app.set('view engine', 'html');
  app.set('views', viewsFolder);

  const apiProxy = createProxyMiddleware(proxyConfig['/api']);
  app.use('/api', apiProxy);

  // 处理所有静态文件请求
  app.get('*.*', express.static(distFolder, {
    maxAge: '1y'
  }));

  // 处理所有页面路由请求,确保触发日志和SSR
  app.get('*', async (req, res) => {
    // 强制输出日志,确保PM2能捕获
    console.log('SSR requested for URL:', req.originalUrl);

    try {
      // 异步获取SSR渲染后的HTML
      const html = await new Promise<string>((resolve, reject) => {
        res.render(index,
          {req, providers: [{provide: APP_BASE_HREF, useValue: req.baseUrl}]},
          (err, renderedHtml) => {
            if (err) reject(err);
            else resolve(renderedHtml);
          }
        );
      });

      // 根据当前URL动态生成OG标签
      const finalHtml = await checkAndAddTags(html, req.originalUrl);
      res.send(finalHtml);
    } catch (err) {
      console.error('SSR render error:', err);
      res.status(500).send('Server Error');
    }
  });

  return app;
}

2. 优化OG元标签动态设置

完善checkAndAddTags函数,根据当前URL匹配页面元数据,动态替换或添加OG标签:

async function checkAndAddTags(html: string, url: string): Promise<string> {
  // 根据URL获取对应页面的元数据(可从API或本地配置表获取)
  const pageMeta = await getPageMetadataByUrl(url);

  // 替换或插入OG标签
  let updatedHtml = html;
  // 设置og:title
  updatedHtml = updateMetaTag(updatedHtml, 'og:title', pageMeta.title);
  // 设置og:description
  updatedHtml = updateMetaTag(updatedHtml, 'og:description', pageMeta.description);
  // 设置og:url
  updatedHtml = updateMetaTag(updatedHtml, 'og:url', `https://your-domain.com${url}`);
  // 设置og:image
  updatedHtml = updateMetaTag(updatedHtml, 'og:image', pageMeta.imageUrl);

  return updatedHtml;
}

// 辅助函数:替换或添加指定meta标签
function updateMetaTag(html: string, property: string, content: string): string {
  const tagRegex = new RegExp(`<meta property="${property}" content=".*?" />`, 'i');
  const newTag = `<meta property="${property}" content="${content}" />`;

  if (tagRegex.test(html)) {
    return html.replace(tagRegex, newTag);
  } else {
    // 若标签不存在,插入到</head>前
    return html.replace('</head>', `${newTag}</head>`);
  }
}

3. PM2日志配置确保输出捕获

创建ecosystem.config.js配置文件,指定日志存储路径:

module.exports = {
  apps: [{
    name: 'monapp-ssr',
    script: 'dist/monapp/server/main.js',
    instances: 1,
    exec_mode: 'fork',
    out_file: './logs/ssr-out.log',
    error_file: './logs/ssr-error.log',
    env: {
      NODE_ENV: 'production'
    }
  }]
};

关键注意事项

  • 生产环境必须确保所有页面请求经过Node服务(PM2),禁止直接返回静态index.html。
  • getPageMetadataByUrl需根据实际路由逻辑实现,可对接后端API或本地路由元数据映射表。
  • PM2启动时需加载上述配置文件,确保日志正常记录。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 15:35:38