Angular16+Ionic+PM2部署后内部页SSR无日志,需动态设置OG元标签
问题解决与代码优化
问题分析
- 生产环境内部页面无日志:大概率是生产环境的反向代理(如Nginx)未将内部页面请求转发至PM2运行的Node服务,直接返回静态
index.html;或是server.ts中静态资源路由与页面路由的匹配逻辑存在冲突,导致内部页面请求未触发SSR路由的日志输出。 - 内部页面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
相关产品推荐
相关产品推荐

