This is a viewer only at the moment see the article on how this works.
To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk
This is a preview from the server running through my markdig pipeline
Wednesday, 26 November 2025
我经营这个博客已经有一段时间了, 写了有关ASP.NET核心、实体框架、HTMX和各种.NET的技术性文章。然而我的搜索排名却... 很低。在调查了为什么之后, 我发现我用一些新手的错误来破坏我自己的SEO。这里是错的,我是如何修补的。
当谷歌爬行你的网站时,它会看到几个关键信号来理解每一页的内容。 我向谷歌发送每页相同的信号。
这是我的 _Layout.cshtml 看起来像:
<meta name="description" content="Scott Galloway is a lead developer and software engineer with a passion for building web applications.">
<meta property="og:description" content="Scott Galloway is a lead developer and software engineer with a passion for building web applications.">
一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、二、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、一、 ASP.NET核心背景服务跟我的主页一样的描述 我深潜进 RAG 建筑结构同样的描述。
Google看到200页以上的页面, 都有相同的描述, 认为「这个网站有重复的内容问题」或「这个网站不关心提供有用的信息」。
我有一个多语种博客,
/blog/my-article (英文)/blog/my-article/fr (法文)/blog/my-article/de (德文)谷歌可能认为这些内容是重复内容, 将SEO值通过多个URL去稀释, 而不是将其合并在主页上。
Google的搜索结果可以显示丰富的片段 - 作者信息、 发布日期、 文章预览。 但是, 只有当您告诉 Google 使用此数据时 。 JSON-LLD结构数据我没有
每页同页 og:image虽然这并不直接影响Google的排名,但如果在社交媒体上分享文章,则会影响点击率,这通过参与信号间接影响SEO。
第一个修正是做出布局支持动态描述,
@{
var currentUrl = $"https://{Context.Request.Host}{Context.Request.Path}";
var defaultDescription = "Scott Galloway is a lead developer and software engineer with a passion for building web applications.";
var pageDescription = ViewBag.Description as string ?? defaultDescription;
}
<!-- Canonical URL -->
<link rel="canonical" href="@currentUrl" />
<!-- Facebook Meta Tags -->
<meta property="og:url" content="@currentUrl">
<meta property="og:type" content="@(ViewBag.OgType ?? "website")">
<meta property="og:title" content="@ViewBag.Title">
<meta property="og:description" content="@pageDescription">
<meta property="og:site_name" content="mostlylucid" />
<!-- Twitter Meta Tags -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="@ViewBag.Title">
<meta name="twitter:description" content="@pageDescription">
<!-- Meta Description -->
<meta name="description" content="@pageDescription" />
<!-- Article metadata for blog posts -->
@if (ViewBag.PublishedDate != null)
{
<meta property="article:published_time" content="@(((DateTime)ViewBag.PublishedDate).ToString("yyyy-MM-ddTHH:mm:ssZ"))" />
<meta property="article:author" content="Scott Galloway" />
}
@if (ViewBag.Categories != null)
{
foreach (var category in ViewBag.Categories)
{
<meta property="article:tag" content="@category" />
}
}
现在布局上写着 ViewBag.Description 设置后, 返回到默认值。 语法 URL 将自动设置为当前页面 URL 。
每个博客文章现在都从最初155个内容字符中产生描述。 Post.cshtml:
@using Mostlylucid.Shared.Helpers
@model Mostlylucid.Models.Blog.BlogPostViewModel
@{
Layout = "_Layout";
ViewBag.Title = $"{Model.Title} ({Model.Language.ConvertCodeToLanguage()})";
// Generate description from plain text content (first 155 chars, truncate at word boundary)
var plainText = Model.PlainTextContent ?? "";
var description = plainText.Length > 155
? plainText.Substring(0, plainText.LastIndexOf(' ', 155)) + "..."
: plainText;
description = description.Replace("\n", " ").Replace("\r", "").Trim();
ViewBag.Description = description;
// Set article metadata
ViewBag.OgType = "article";
ViewBag.PublishedDate = Model.PublishedDate;
ViewBag.Categories = Model.Categories;
// Build canonical URL (without language suffix for English)
var canonicalUrl = Model.Language == "en"
? $"https://{Context.Request.Host}/blog/{Model.Slug}"
: $"https://{Context.Request.Host}/blog/{Model.Slug}/{Model.Language}";
}
这里的要点是:
og:type “物品”改为“物品” - 告诉社会平台,这是一篇文章,不是通用网页这是丰富片段的大块。 在您的博客文章视图中添加一个 JSON- LD 脚本块 :
<!-- JSON-LD Structured Data for Blog Post -->
<script type="application/ld+json">
{
"@@context": "https://schema.org",
"@@type": "BlogPosting",
"headline": "@Model.Title",
"description": "@description",
"datePublished": "@Model.PublishedDate.ToString("yyyy-MM-ddTHH:mm:ssZ")",
@if (Model.UpdatedDate.HasValue)
{
@:"dateModified": "@Model.UpdatedDate.Value.ToString("yyyy-MM-ddTHH:mm:ssZ")",
}
"author": {
"@@type": "Person",
"name": "Scott Galloway",
"url": "https://mostlylucid.net/blog/aboutme"
},
"publisher": {
"@@type": "Organization",
"name": "mostlylucid",
"logo": {
"@@type": "ImageObject",
"url": "https://mostlylucid.net/img/logo.svg"
}
},
"mainEntityOfPage": {
"@@type": "WebPage",
"@@id": "@canonicalUrl"
},
"wordCount": @Model.WordCount,
"inLanguage": "@Model.Language",
"keywords": "@string.Join(", ", Model.Categories)",
"image": "https://mostlylucid.net/img/social2.jpg"
}
</script>
注注: @@ 逃离 @ Razor 视图中的符号 - JSON-LD 用途 @context 和 @type 否则将被解释为Razor语法。
这个结构化数据告诉谷歌:
Google可以用这个来显示搜索结果中的丰富片段,包括作者信息、公布日期等。
不要忘了你的静态页面,每个页面都应该有一个独特的相关描述:
<!-- Home page -->
@{
ViewBag.Title = "mostlylucid- Scott Galloway's Developer Blog";
ViewBag.Description = "Technical blog covering ASP.NET Core, C#, Entity Framework, HTMX, Docker, and modern web development. Practical tutorials, NuGet packages, and open source projects.";
}
<!-- Blog index -->
@{
ViewBag.Title = "Blog Posts";
ViewBag.Description = "Technical articles on ASP.NET Core, C#, Entity Framework, Docker, and modern web development. Practical tutorials and real-world examples from a lead developer.";
}
<!-- Contact page -->
@{
ViewBag.Title = "Contact Scott Galloway";
ViewBag.Description = "Get in touch with Scott Galloway. Questions about ASP.NET Core, C#, web development, or collaboration opportunities? Send me a message.";
}
<!-- Search page -->
@{
ViewBag.Title = "Search Results";
ViewBag.Description = "Search through technical articles on ASP.NET Core, C#, Entity Framework, and web development. Find tutorials, guides, and solutions.";
}
您需要一个站点图。 这里有一个简单的控制器, 动态生成一个 :
public class SiteMapController(
IBlogViewService blogViewService,
IHttpContextAccessor httpContextAccessor) : Controller
{
[HttpGet]
[ResponseCache(Duration = 43200)] // Cache for 12 hours
public async Task<IActionResult> Index()
{
var pages = await blogViewService.GetPosts();
var siteUrl = $"https://{httpContextAccessor.HttpContext?.Request.Host}";
XNamespace sitemap = "http://www.sitemaps.org/schemas/sitemap/0.9";
var feed = new XDocument(
new XDeclaration("1.0", "utf-8", null),
new XElement(sitemap + "urlset",
from page in pages
select new XElement(sitemap + "url",
new XElement(sitemap + "loc", $"{siteUrl}/blog/{page.Slug}"),
new XElement(sitemap + "lastmod", page.PublishedDate.ToString("yyyy-MM-dd")),
new XElement(sitemap + "changefreq", "weekly"),
new XElement(sitemap + "priority", "0.8")
)
)
);
return Content(feed.ToString(), "text/xml");
}
}
登记路线:
app.MapControllerRoute(
name: "sitemap",
pattern: "sitemap.xml",
defaults: new { controller = "SiteMap", action = "Index" });
告诉搜索引擎你的站点图在哪里,
app.MapGet("/robots.txt", async context =>
{
var siteUrl = $"https://{context.Request.Host}";
var robotsTxt = $"""
User-agent: *
Allow: /
Sitemap: {siteUrl}/sitemap.xml
""";
context.Response.ContentType = "text/plain";
await context.Response.WriteAsync(robotsTxt);
});
许多开发商使用 RSS 阅读器。
<link rel="alternate" type="application/atom+xml"
title="RSS Feed for mostlylucid.net"
href="https://mostlylucid.net/rss" />
您可能想知道如何为每个文章生成独特的 OG 图像。 对于一个技术博客来说, 这可能不值得努力。 您的流量来自 :
一致的品牌形象对识别来说是件好事。 传统形象的生成对于视觉内容、新闻网站或营销页面来说更为重要,因为它们在Facebook和Twitter上竞相点击。
如果您真的想要生成它们, 您可以使用 图像共享 在模板上覆盖文本( 已经用于图像处理) :
public async Task<string> GenerateOgImage(string title, string slug)
{
using var image = await Image.LoadAsync("wwwroot/img/og-template.png");
var font = SystemFonts.CreateFont("Arial", 48, FontStyle.Bold);
image.Mutate(x => x.DrawText(
new RichTextOptions(font)
{
Origin = new PointF(50, 200),
WrappingLength = 1100
},
title,
Color.White));
var outputPath = $"wwwroot/og/{slug}.png";
await image.SaveAsPngAsync(outputPath);
return $"/og/{slug}.png";
}
但对于一个技术博客来说呢?
使用使用 Google的丰富结果测试 来验证您的结构化数据。粘贴一个 URL , 它会告诉您您的 JSON- LD 是否有效, 您有资格获得什么丰硕的结果 。
最简单的测试 - 查看页面源并检查 :
<meta name="description"> 此页面的独特性吗 ?<link rel="canonical">?<script type="application/ld+json"> 块吗 ?部署后,向 Google 搜索控制台。您也可以使用 URL 检查工具来查看谷歌如何看待您的页面并要求重新索引。
解决办法直截了当:
问题 修补 修补 修补 修补 修补 修补 修补 修补 修补 修补 修补 修补 修补 修补 修补 修补 修补 修补 修补 修补 修补 修补 修补 修补 修补 修补 修补 修补 修补 修补 修补 修补 修补 修补 修补 修补 修补 修 修补 问题 修补 修 修补 修 修 修 修 修 修 修 修 修 修补 修 修 修补 修 修 修补 修 修 修 修 修 修 修 修 修 修 修 修 修 修 修 修补 修 修 修 修 修 修 修 修 修 修 修 修 修 修补 修
|---------|-----|
静态元说明 动态 ViewBag.Description 由内容自动生成 {
<link rel="canonical"> 到布局
BlogPosting 每个柱子上都有计谋,
缺少文章元数据 article:published_time, article:author, article:tag 元标记 *
固定页面描述 * * 家庭、博客、联系人、搜索页面的独特描述 *关键的洞察力: 谷歌无法读懂你的心。如果每页都有相同的描述,谷歌无法知道是什么使每页具有独特性和价值。告诉谷歌每页都是什么,你会得到更好的排名。
让Google几周后重新搜索你的网站,你应该看到改进。SEO是一个漫长的游戏,但这些基本原理是其他一切基础的基础。
© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.