阅读视图

发现新文章,点击刷新页面。

浅谈前后端分离系统的SEO优化

开发一个系统,不管是从头开始,还是在已有系统上二次开发,从来都不是一蹴而就的事情。在上线以前总觉得已经做够了足够的测试,但是在上线之后还是会出现各种各样的问题。

有的问题,如果是新系统完全可以避免,正是由于是在已有系统上开发的为了兼容wp才会引入一系列的问题,这类问题主要是wp原生的一些机制兼容问题导致的包括但不限于:

1.wp固定连接的兼容

2.shortcode的解析处理

3.wp资源文件与新系统资源文件的路径兼容处理

4.wp启用插件的功能实现,邮件通知、micro-post、邮件发送、邮件模板等等

5.其他的未知问题

也有一部分是新系统天生的缺陷:seo不友好,搜索引擎爬虫无法获取网页内容,毕竟robot不会执行js,这个是前后端分离系统的必然缺陷。

<!doctype html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8" />
    <link
      rel="icon"
      href="https://zhongxiaojie.cn/wp-content/uploads/2026/01/uugai.com-166111691272754-100x100.png"
      sizes="32x32"
    />
    <link
      rel="icon"
      href="https://zhongxiaojie.cn/wp-content/uploads/2026/01/uugai.com-166111691272754-200x200.png"
      sizes="192x192"
    />
    <link
      rel="apple-touch-icon"
      href="https://zhongxiaojie.cn/wp-content/uploads/2026/01/uugai.com-166111691272754-200x200.png"
    />
    <meta
      name="msapplication-TileImage"
      content="https://zhongxiaojie.cn/wp-content/uploads/2026/01/uugai.com-166111691272754-300x300.png"
    />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta
      name="description"
      content="爱好广泛的女王 独立APP开发者 AI修理师 爬虫砖家 逆向工程师 人工智能 全栈工程师"
    />
    <meta
      name="keywords"
      content="人工智能,机器学习,ml,逆向分析,信息安全,物联网,ida,uniapp,python,爬虫,妹子图,秀人集,java,vue"
    />
    <meta
      name="theme-color"
      content="#ff4f87"
    />
    <link
      rel="manifest"
      href="/manifest.json"
    />
    <link
      rel="stylesheet"
      href="/vendor/enlighterjs.min.css"
    />
    <link
      rel="stylesheet"
      href="/vendor/simple-microblogging.css"
    />
    <title>obaby 𝐢‍𝐧⃝ void - 程序媛 / 独立开发者 / 智商不稳定的女神经</title>
    <script type="module" crossorigin src="/assets/index-DFHpxK1A.js"></script>
    <link rel="stylesheet" crossorigin href="/assets/index-CKljzL1r.css">
  </head>
  <body>
    <div id="app"></div>
    <script
      defer
      src="/vendor/enlighterjs.min.js"
    ></script>
    <script defer src="/vendor/obaby.js"></script>

  </body>
</html>

 

当然有人会比较在意这个东西,不是说这个东西不对。可能是自己没那么在乎吧,之前就曾经收到过数次关于seo友链不显示的问题,上次是搞页面静态化。

其实,在我的博客添加的友链,也并不是全部都不显示,毕竟还有其他的域名,zhongxiaojie.com 以及 oba.by等还是会显示完整的友链信息,这两个域名并没有切换到新的前后端分离的系统。所以,我博客的友链,相当于数个站都给友链做了多次链接,我不知道这个东西对于seo有没有作用,至于是有好处,还是有坏处,我并不清除,我自己并不是那么关注所谓的seo。如果觉得这样反而会出问题的,欢迎反馈,我会及时删除相关链接哈。

当然,这个东西有办法解决吗?答案自然是有,至于解决方法,那就是继续回归服务器渲染。

这解决方案真的是简单粗暴啊,合着这折腾来折腾去,又要弄回服务器渲染,这辛辛苦苦四十年,一夜回到解放前?

采用这种简单粗暴的方法来解决seo问题,显示不是本仙女的作风。既然是针对搜索引擎的,那就直接对搜索引擎做单独的处理就完了。检测ua,如果是收缩引起的ua返回服务器渲染之后的内容,如果是正常浏览(搜索引擎爬虫意外的ua)返回前后端分离的内容。

要实现服务器渲染,基于vue的可以参考nuxt.js(百度百科):

Nuxt.js是由NuxtLabs团队于2016年10月推出的基于Vue.js的开源Web框架,采用MIT License授权。该框架灵感来源于Next.js,Nuxt采用了约定俗成的规范以及一种明确的目录结构,以实现对重复性任务的自动化处理,并使开发人员能够专注于推进新功能的开发。 [2] [5] [8]
Nuxt默认内置服务器端渲染(SSR)功能、支持静态站点生成(SSG)和单页面应用(SPA)三种部署模式,可通过”nuxt generate”命令生成预渲染HTML文件实现静态化部署 [5] [7]。采用模块化架构提供50多个扩展模块,支持TypeScript类型安全、推送和现代化开发工具链 [4] [6]

接下来也就简单了,创建nuxt项目,实现与frontend同样的页面路由和相关的页面文件布局。接口可以直接复用当前的接口,

配置openresty的处理逻辑:

# -----------------------------------------------------------------------------
# Dynamic Rendering(SEO):爬虫 UA → Nuxt SSR;普通用户 → 现有 SPA
# - Nuxt SSR 服务建议监听 127.0.0.1:3000(可按需调整)
# - ?__ssr=1 可强制走 SSR(方便自测/排障)
# - 仅对“页面路由”生效,不影响 /assets、/vendor、/bp-api、WP 后台等
# -----------------------------------------------------------------------------
set $bp_force_ssr 0;
if ($arg___ssr = "1") {
    set $bp_force_ssr 1;
}

set $bp_is_bot 0;
if ($http_user_agent ~* "(googlebot|bingbot|baiduspider|yandexbot|duckduckbot|slurp|sogou|360spider|bytespider|petalbot|facebookexternalhit|twitterbot|rogerbot|ahrefsbot|semrushbot|mj12bot)") {
    set $bp_is_bot 1;
}

location @nuxt_ssr {
    proxy_pass http://127.0.0.1:3000;
    proxy_http_version 1.1;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Forwarded-Host $host;
    proxy_set_header X-Forwarded-Uri $request_uri;
}

# 418 跳转技巧:在页面路由里 return 418 → error_page 转到 @nuxt_ssr
error_page 418 = @nuxt_ssr;

启动之后就可以查看服务器渲染的页面了:

当然,这个实现方法的缺点就是得完全复刻frontend的相关路由和页面,优点就是不用关注原来的系统实现逻辑,哪怕爬虫seo系统出问题也不会影响现有的系统运行。

 

产品,还是玩具? — Baby Press(缝合怪)

这算是给这个东西写的第二篇正式的文章,本来我的想法很简单,做一个简单的前后端分离的系统来完全替代wp的php渲染机制。

只是,在开发的过程中为了迎合wp的各种现有数据格式、插件、主题、shortcode等等,代码复杂度也在不断的提高。得益于ai的崛起,现在生成代码是真的简单方便,原来数个人的工作,现在一人就可以完成了。尽管哪怕没有ai,我自己也能全部搞定。ai在某些方便还是提高了输出效率,原本很多人不是全栈的,现在也给搞成了全干工程师,哪怕不会,也得硬着头皮上,去验证ai写的各种代码。

我一般不喜欢给ai太具体的描述,但是会给一个准确的描述,实现方法,实现路径,实现目标,所以多数时候ai呈现的代码质量尚可。然而,等到实际上线的时候发现还是一堆问题。

做完准备把wp的前端全部迁移到现在的baby press的前端,尝试部署之后出现了一系列问题,当然很多问题源自于测试不充分。为了解决两个系统的整合问题,需要大量的配置文件和代码。除了openresty的配置文件,前后端也生成了一堆默认的配置模板,当然,这些模板主要是为了提供一些自定义的功能,以及安全性提升加密等等。

这么复杂的系统,现在我觉得更像一个玩具,而不是产品,好的产品应该是简单易用,开箱可用的。

DJANGO_SECRET_KEY=dev-secret-key-change-me
DJANGO_DEBUG=1
DJANGO_ALLOWED_HOSTS=127.0.0.1,localhost
# 浏览器里「页面」的 origin(协议+域名+端口),须与前端访问地址一致;逗号分隔、勿加路径。
# 生产示例(Vue 部署在 i 子域、API 在 api 子域时,必须把 i 子域写进来,否则会 CORS 失败):
# CORS_ALLOWED_ORIGINS=http://127.0.0.1:5173,http://localhost:5173,http://i.zhongxiaojie.cn,https://i.zhongxiaojie.cn
CORS_ALLOWED_ORIGINS=http://127.0.0.1:5173,http://localhost:5173
# Django CSRF 信任来源(协议+域名+端口,逗号分隔;用于 /admin/login/ 等表单提交)
# 生产示例:CSRF_TRUSTED_ORIGINS=https://api.zhongxiaojie.cn,https://i.zhongxiaojie.cn
CSRF_TRUSTED_ORIGINS=http://127.0.0.1,http://localhost

# Django 缓存(评论 UA/IP 查询结果);推荐 Redis,例如 redis://127.0.0.1:6379/1
# 留空则使用 LocMem(仅开发、单进程)
# DJANGO_CACHE_REDIS_URL=redis://127.0.0.1:6379/1
#
# WordPress Object Cache Pro(可选):Django 直写评论后用于定向清理评论缓存。
# 请与 WordPress 端 WP_REDIS_CONFIG 的 host/db/prefix 保持一致。
# 例如 WP_REDIS_CONFIG 里 database=5,则这里应为 redis://127.0.0.1:6379/5
# WP_OBJECT_CACHE_REDIS_URL=redis://127.0.0.1:6379/<database>
# 注意:当前定向清理实现依赖 prefix,建议在 WP_REDIS_CONFIG 中显式配置 'prefix' => 'zhxj'
# WP_OBJECT_CACHE_REDIS_PREFIX=zhxj
# WP_OBJECT_CACHE_BLOG_ID=0

# Baby IP Lookup:本机 lookup-ua 与静态资源公网域名(PNG/SVG 补全)
# UA_LOOKUP_UPSTREAM_BASE_URL=http://127.0.0.1:18765
# UA_LOOKUP_PUBLIC_ASSETS_BASE_URL=https://ip.zhongxiaojie.cn
# UA_LOOKUP_DEFAULT_METHOD=ip2location
# UA_LOOKUP_CACHE_TTL=604800

# WordPress database connection (MySQL/MariaDB)
WP_DB_NAME=wordpress
WP_DB_USER=root
WP_DB_PASSWORD=
WP_DB_HOST=127.0.0.1
WP_DB_PORT=3306

# WordPress table prefix, e.g. wp_ / wp123_
WP_TABLE_PREFIX=wp_

# 是否信任反代/CDN 转发头(CF-Connecting-IP / X-Real-IP / X-Forwarded-For),默认开启。
# - 生产推荐开启,并配置 TRUSTED_PROXY_IP_RANGES,只信任你的网关/CDN 回源 IP 段
# - 若 API 不会被公网直连,且 CDN 回源 IP 经常变:可保持开启并留空 TRUSTED_PROXY_IP_RANGES(有伪造风险)
TRUST_PROXY_HEADERS=1
# 反代终止 TLS(如 Nginx/Edge/CDN)时建议开启,配合 X-Forwarded-Proto 识别 https
SECURE_PROXY_SSL_HEADER_ENABLED=1

# 额外输出“真实 IP access log”(Daphne 的 access log 里显示的是 CDN 节点 IP)
# 打开后会在 stdout 输出形如:[realip] ip=... remote=... status=... GET /api/...
REAL_IP_ACCESS_LOG_ENABLED=0

# 受信任反向代理 / CDN 的 IP 段(CIDR,逗号分隔)。
# 仅当请求来源 REMOTE_ADDR 命中这些 IP 段时,后端才会信任 CF-Connecting-IP / X-Real-IP / X-Forwarded-For。
# - 本机 Nginx 反代:127.0.0.1/32,::1/128
# - 生产:把你的 Nginx/网关内网地址段、或 CDN 回源 IP 段加入这里
TRUSTED_PROXY_IP_RANGES=127.0.0.1/32,::1/128

# API 请求签名(HMAC + ts + nonce)——默认关闭
# 注意:这是“请求验签”,不是“返回加密”。建议仅在 HTTPS 下启用。
# API_SIGNING_ENABLED=1
# API_SIGNING_SECRET=change-me-long-random
# 允许客户端时间漂移(秒),超出即拒绝(防离线重放)
# API_SIGNING_TTL_SECONDS=60
# nonce 去重缓存 TTL(秒),建议 >= API_SIGNING_TTL_SECONDS
# API_SIGNING_NONCE_TTL_SECONDS=300
# 需要签名的路径前缀(逗号分隔)
# API_SIGNING_REQUIRED_PREFIXES=/api/
# 免签路径(逗号分隔,严格 path 匹配),例如健康检查:
# API_SIGNING_EXEMPT_PATHS=/api/health/,/api/ping/

# SMTP / Email backend (Django)
# 不配置则不会真的发出邮件(除非你使用本地控制台邮件后端等)。
# EMAIL_BACKEND=django.core.mail.backends.smtp.EmailBackend
# EMAIL_HOST=smtp.example.com
# EMAIL_PORT=587
# EMAIL_USE_TLS=1
# EMAIL_HOST_USER=your-account@example.com
# EMAIL_HOST_PASSWORD=your-app-password
# DEFAULT_FROM_EMAIL="obaby <no-reply@zhongxiaojie.cn>"
#
# 评论回复邮件通知(前台回复他人评论时)
# COMMENT_REPLY_NOTIFICATION_ENABLED=1
# COMMENT_REPLY_EMAIL_FROM="obaby <no-reply@zhongxiaojie.cn>"
# COMMENT_REPLY_EMAIL_HEADER_IMAGE_URL=https://zhongxiaojie.com/wp-content/uploads/2026/01/uugai.com_1661691241113463.png
# COMMENT_REPLY_EMAIL_HEADER_IMAGE_WIDTH=520
# COMMENT_REPLY_EMAIL_HEADER_IMAGE_HEIGHT=180
# COMMENT_REPLY_EMAIL_HEADER_ALT=obaby 𝐢‍𝐧⃝ void
# COMMENT_REPLY_EMAIL_FOOTER_LINE1=obaby 𝐢‍𝐧⃝ void
# COMMENT_REPLY_EMAIL_FOOTER_LINK_TEXT=oba.by
#
# 与 WordPress CREN 插件退订链接校验一致(取自 wp-config.php)
# WORDPRESS_AUTH_KEY=
# WORDPRESS_AUTH_SALT=
# 与 WordPress 登录 Cookie(wordpress_logged_in_*)校验一致(同样取自 wp-config.php)
# 推荐配置 LOGGED_IN_KEY / LOGGED_IN_SALT;留空时后端会回退到 AUTH_KEY / AUTH_SALT
# WORDPRESS_LOGGED_IN_KEY=
# WORDPRESS_LOGGED_IN_SALT=

# 服务器状态小组件:统计磁盘路径(Linux "/";Windows "C:\\")
# SERVER_PROBE_DISK_PATH=/

# 
列表头像:Gravatar 兼容镜像根(路径同 /avatar/{md5}?s=&d=),默认 gg.lang.bi # GRAVATAR_AVATAR_BASE_URL=https://gg.lang.bi # 侧边栏「近期文章」:正文无图时的缩略图回退地址 # SIDEBAR_RECENT_POST_FALLBACK_IMAGE_URL=https://zhongxiaojie.cn/wp-content/uploads/2026/01/... # 评论反垃圾分类(可选;不配置则不调服务、新评论直接通过) # BABY_ANTI_SPAM_CLASSIFY_URL=http://192.168.1.8:8765/v1/classify # BABY_ANTI_SPAM_SECRET=change-me-long-random # BABY_ANTI_SPAM_TIMEOUT=3 # 同一邮箱+IP 对同一篇文章连续提交的最短间隔(秒,0 关闭,最大 120);依赖 Django cache # COMMENT_SUBMIT_COOLDOWN_SECONDS=0 # 前台文章评论列表分页(GET /api/wp/posts/:id/comments/):按一级评论(线程)分页,每页含该层全部回复;不传 page 时默认最后一页(最新线程) # WP_COMMENTS_PER_PAGE=50 # 客户端 ?per_page= 的上限(不超过 500) # WP_COMMENTS_MAX_PER_PAGE=200 # 顶层线程展示:desc=递减(最新在上,默认);asc=递增(最新在下) # WP_COMMENTS_ORDER=desc # Nginx FastCGI 缓存:评论审核通过(comment_approved=1)后清理文章页、首页(可选分类页) # 与 WordPress 插件「Nginx FastCGI Cache Purge on Comment」类似:HTTP GET {站点}/purge{路径} # NGINX_CACHE_PURGE_ENABLED=1 # NGINX_PURGE_PUBLIC_BASE_URL=https://你的域名 # NGINX_PURGE_TIMEOUT=2 # NGINX_PURGE_SSL_VERIFY=1 # NGINX_PURGE_CATEGORIES=1 # NGINX_CACHE_FILES_PATH=/var/cache/nginx/allinone # Kama WP Smile:评论表情包资源(给前端下发,避免硬编码域名) # 若留空,前端会回退使用自身默认/环境变量配置。 # SMILE_PACK_BASE_URL=https://zhongxiaojie.cn/wp-content/plugins/kama-wp-smile-packs/qip_dark_all/ # SMILE_PACK_EXT=gif # SMILE_PACK_TOKENS=smile,sad,laugh,rofl,blum,kiss,yes,no,good,bad,unknw,sorry,pardon,wacko,acute,boast,boredom,dash,search,crazy,yess,cool,air_kiss,angel,bb,beach,aggressive,blush,bomb,bravo,buba,bye,cry,curtsey,dance,dash2,declare,diablo,don-t_mention,drinks,focus,fool,friends,gamer,give_rose,heart,help,hi,laugh1,mail,mda,mosking,music,negative,ok,popcorm,punish,rtfm,sarcastic,secret,shock,shout,thank_you,vava,victory,beee,big_boss,wink,yu,cray2,dash3,girl_pinkglassesf,girl_prepare_fish,locomotive,lazy2,agree,feminist,fuk,fuck,jester,hunter,moil,offtopic,paladin,shablon_01,spam,vinsent,warning,yahoo,superman,girl_witch,fans,beta,butcher,elf,first_move,gamer2,girl_cray2,girl_cray,girl_blum,girl_dance,girl_crazy,girl_haha,heat,hysteric,nhl_crach,nhl_fight,pig_ball,aikido,angry2,banned,alcoholic,bb2,flood,gamer3,girl_devil,flirt,girl_cray3,girl_drink,girl_hide,girl_hospital,girl_impossible,girl_in_love,girl_mad,girl_sad,girl_sigh,girl_smile,girl_to_take_umbrage,girl_wacko,lazy1,nono,man_in_love,party,scenic,queen,paint,crazy_pilot,dwarf,hang1,haha,grin,good3

好处呢,就是所有的系统配置基本都在这个配置文件中控制即可,无需去各种地方设置了,修改之后重启服务即可。

之所以说是玩具,其实我在wp之外添加了另外一个简单的管理后台,这也是为什么选了django 而没有直接用fastapi。

这个东西最初的目的也不是为了替换wp,所以很多功能也没必要再实现一遍了。基础的操作还是在wp的后台完成。

当然,做完折腾到零点多,补全了一些功能之后,最终还是上线了,这就是目前看到的页面效果,lighthouse测试:

ipv4测试:

ipv6测试:

对于wp的主题,也修改了下页面宽度,与现在的vue的页面宽度基本一致了:

http://zhongxiaojie.com

代码地址:

https://gitee.com/obaby/baby-press-public

❌