同一套 Astro SSR 跑多个域名,我是怎么处理 Host、D1 和缓存串站的
同一套 Astro SSR 跑多个域名我是怎么处理 Host、D1 和缓存串站的前段时间我在折腾一个 Astro 内容站。最开始想得很简单页面模板都一样只是域名、站名、栏目和配色不同那就让一套代码多绑几个域名省得每个站复制一份项目。真正做起来才发现页面复用反倒是最容易的部分。麻烦的是 Host 怎么识别、Cloudflare 缓存会不会串、Canonical 会不会写到另一个域名以及内容多了以后 Sitemap 怎么拆。现在项目已经跑在 Cloudflare Workers 上目录数据放 D1图片放 R2。我放一个实际页面方便对照本文说的 SSR 输出和链接结构92电影网。这篇不讲完整建站流程只记几个我自己改了不止一次的地方。先把站点差异收进配置不要散落在页面里第一版里我差点在组件里到处写域名判断。写两三个还能看继续加下去首页、详情页和 Sitemap 各有一套判断迟早对不上。后来我把站点相关的东西都收进SiteConfiginterfaceSiteConfig{domain:string;brand:string;routeKey:string;urlKey:string;tagline:string;accent:string;categories:Array{slug:string;label:string;};}域名、品牌名、路由 key、主题色和栏目都是数据。页面组件只关心“当前是哪一个配置”不再关心“当前是哪一个域名”。这个改动不复杂但后面做缓存、SEO 和数据库查询时都省了事因为它们拿到的是同一个site对象。Host 解析放在 Astro Middleware我的处理方式是在请求刚进来时解析 hostname去掉www和端口再到 Map 里查配置。constbyDomainnewMap(sites.map((site)[site.domain,site]));functiongetSiteByDomain(hostname:string){constnormalizedhostname.toLowerCase().split(:)[0].replace(/^www\./,);returnbyDomain.get(normalized);}exportconstonRequestdefineMiddleware(async(context,next){consturlnewURL(context.request.url);constsitegetSiteByDomain(url.hostname);if(!site){returnnewResponse(Unknown site,{status:404,headers:{x-robots-tag:noindex, nofollow,},});}context.locals.sitesite;returnnext();});之后在 Astro 页面里直接读Astro.locals.site。根域名和 HTTP 我统一 301 到https://www。一开始觉得这只是访问习惯问题后来检查页面才意识到如果几个版本都能返回 200Canonical、站内链接和外部抓取很容易出现不一致。if(url.hostnamesite.domain||url.protocol!https:){url.protocolhttps:;url.hostnamewww.${site.domain};returnResponse.redirect(url,301);}Workers 的预览域名我单独放行做测试但会加noindex。生产环境里遇到没有配置过的 Host直接 404不让它套用默认站点。D1 和 R2 的分工尽量简单目录数据在 D1 里保存标题、简介、地区、年份、集数以及图片对象键。图片本身放 R2。wrangler.jsonc中只声明 binding实际项目里的数据库 ID 等信息这里就不贴了{d1_databases:[{binding:DB,database_name:content-catalog}],r2_buckets:[{binding:POSTERS,bucket_name:content-assets}]}页面不直接拼 R2 存储地址而是统一走站内的/media/路由。这样以后换存储方式内容表和页面模板都不用跟着改。图片基本不会变我给/media/一年缓存if(pathname.startsWith(/media/)){returnpublic, max-age31536000, immutable;}HTML 就不能这么干。首页和栏目会更新用短缓存详情页稍长一点搜索页带用户输入直接private, no-store。最容易出问题的其实是缓存键多域名共用一个 Worker 时我最担心的是串站。比如两个域名都请求/如果自己做缓存键时只保留 pathname那么后缓存的页面可能被另一个域名直接拿走。页面看着还能打开但 title、Canonical、品牌名和内链已经全错了这种问题比报错更难发现。我最后没有单独拿 pathname 拼 key而是直接使用原始 RequestconstedgeCacheawaitcaches.open(content-sites-v1);constcachedawaitedgeCache.match(context.request);if(cached){returncached;}constresponseawaitnext();if(response.ok){awaitedgeCache.put(context.request,response.clone());}returnresponse;完整请求 URL 本身带 Host不同域名自然分开。当然也不是所有 GET 都适合进缓存。我排除了搜索页、带 Authorization 的请求、明确要求no-cache的请求以及中转媒体请求。缓存只是加速手段不能反过来改变页面应该有的行为。为了排查方便我还会在响应头里写x-movier-cache: HIT或MISS。浏览器里一眼能看到比凭感觉猜 Worker 到底有没有命中省事。SEO 信息不能写成一套公共常量多域名项目里Canonical 写死是个低级但很实际的错误。我先根据站点配置得到主 OriginfunctionprimaryOrigin(site:SiteConfig){returnhttps://www.${site.domain};}每个页面再用当前路径生成自己的 Canonicalconstcanonical${primaryOrigin(site)}${pathname};首页输出WebSite详情页按内容类型输出TVSeries栏目页输出ItemList。搜索页我设为noindex,follow因为搜索参数组合太多没必要让它们和正式栏目、详情页抢索引。Sitemap 也碰到过同样的问题。内容少时一个 XML 就够了数据多起来后我改成 Sitemap Index固定页面放main.xml详情 URL 每 2000 条拆一个文件。constpagesMath.max(1,Math.ceil(total/2000));constentries[${origin}/sitemaps/main.xml,...Array.from({length:pages},(_,index)${origin}/sitemaps/items-${index1}.xml,),];这里的origin一定来自当前 Host 对应的站点配置。否则 Sitemap 能正常打开里面却可能混着另一个域名检查不仔细很难发现。我现在上线前怎么检查我不会只看首页能不能打开而是同时开两个不同域名请求相同路径对照 title、Canonical、内链和缓存响应头。然后再看根域名是否只跳一次、搜索页有没有进缓存、预览域名有没有noindex以及 Sitemap 里有没有混入其他 Host。另一个容易漏掉的情况是空栏目。数据库暂时没有匹配内容时与其照常输出一个可以索引的空页面不如先设成noindex,follow也不要放进 Sitemap。等有真实内容再开放索引。回头看这个项目最花时间的地方并不是 Astro 组件而是保证“当前站点身份”从请求进来到页面输出始终一致。Host 解析、D1 查询、缓存键、Canonical 和 Sitemap 只要有一处用了另一套判断线上就会出现很别扭的问题。目前这套方案跑下来比较稳定。以后再加栏目或改页面我只动公共组件和站点配置不用维护很多份差不多的代码。对于内容结构接近、又确实需要独立域名和独立 SEO 信息的项目这种做法还是挺省心的。

相关新闻

完整指南:D2DX暗黑破坏神2高清补丁的三大核心功能与快速安装教程

完整指南:D2DX暗黑破坏神2高清补丁的三大核心功能与快速安装教程

完整指南:D2DX暗黑破坏神2高清补丁的三大核心功能与快速安装教程 【免费下载链接】d2dx D2DX is a complete solution to make Diablo II run well on modern PCs, with high fps and better resolutions. 项目地址: https://gitcode.com/gh_mirrors/d2/d2dx …

2026/11/13 7:25:22 阅读更多 →
《仙剑奇侠传》游戏资源修改指南:零基础实现图片、文本与数值定制

《仙剑奇侠传》游戏资源修改指南:零基础实现图片、文本与数值定制

1. 先搞清楚“不会编程也能改”到底指的是什么看到这个标题,很多人第一反应可能是“不用写代码就能修改游戏内容”。但这里的“改”更准确地说,是指对现有游戏资源进行替换、调整或简单定制,而不是从零开发新功能。这类工具通常面向的是对游戏…

2026/11/5 22:49:40 阅读更多 →
如何高效部署智能求职自动化系统:全平台简历自动投递实战指南

如何高效部署智能求职自动化系统:全平台简历自动投递实战指南

如何高效部署智能求职自动化系统:全平台简历自动投递实战指南 【免费下载链接】get_jobs 💼【AI找工作助手】全平台自动投简历脚本:(boss、前程无忧、猎聘、智联招聘) 项目地址: https://gitcode.com/gh_mirrors/ge/get_jobs 还在为每…

2026/11/11 0:40:20 阅读更多 →

最新新闻

Electron桌面应用开发实战:从环境搭建到打包发布全流程

Electron桌面应用开发实战:从环境搭建到打包发布全流程

做前端这些年,我大部分时间都在跟浏览器标签页打交道,直到第一次把桌面应用跑起来,才发现原来自己一直被限制在地址栏和刷新按钮围起来的框框里。Electron这个词,最开始是在某段时间频繁出现在同事的讨论里,一个用HTML…

2026/11/13 5:13:25 阅读更多 →
滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

简介:亚太天能T109滑盖智能锁产品说明书是一份PDF格式的官方技术文档,主要面向智能锁安装人员、终端用户及售后维护者,帮助快速掌握产品安装、配置与常见故障处理。全包仅1个PDF文件,大小4.37MB,内容结构清晰&#xff…

2026/11/13 5:13:25 阅读更多 →
PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

上个月我在调试一台高速AGV的驱动系统,整车要求空载跑到8m/s,电机转速已经拉到额定点,驱动器的输出电压也顶到了母线电压极限附近。再往上踩速度指令,转速纹丝不动,三相电流反而开始震荡。那会儿我的第一反应就是&…

2026/11/13 5:13:25 阅读更多 →
C++核心知识点:从内存管理到STL的实战指南

C++核心知识点:从内存管理到STL的实战指南

C 这门语言我前前后后折腾了十几年,用它写过嵌入式驱动、做过游戏服务端,也搭过基础组件库。每次看到新人一头扎进"C 核心知识点"这个搜索词,我其实挺感慨的——这门语言的难点从来不是语法多,而是知识点之间的耦合太深…

2026/11/12 18:22:26 阅读更多 →
T109滑盖智能锁怎么装?安装调试与故障排查全攻略

T109滑盖智能锁怎么装?安装调试与故障排查全攻略

简介:亚太天能T109滑盖智能锁产品说明书以PDF格式发布,适合智能锁用户、安装人员与售后维修人员阅读。内容围绕T109系列指纹密码刷卡锁展开,先介绍前面板、后面板、锁体、机械钥匙、电池盒盖等部件名称与功能,再给出完整安装步骤&…

2026/11/12 18:22:25 阅读更多 →
LLM知识降熵:从信息垄断到理解权下放

LLM知识降熵:从信息垄断到理解权下放

你有没有过这种经历:翻开源码注释、论文摘要或者一份专业报告,每个字都认识,连在一起却像天书。想找人聊聊,身边要么没人懂,要么懂的懒得跟你解释。后来我试着把同一段内容丢给一个大语言模型,几秒钟之后&a…

2026/11/13 5:13:25 阅读更多 →

日新闻

周新闻

月新闻