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
Jag har kört den här bloggen ett tag nu, skriva detaljerade tekniska artiklar om ASP.NET Core, Entity Framework, HTMX, och alla typer av .NET godhet. Ändå min sökning ranking var ... underwhelming. Efter att äntligen undersöka varför, upptäckte jag att jag hade sabotera min egen SEO med några nybörjar misstag. Här är vad som var fel och hur jag fixade det.
När Google kryper din webbplats, det tittar på flera viktiga signaler för att förstå vad varje sida handlar om. Jag skickade Google samma signaler för varje enskild sida.
Här är vad min _Layout.cshtml såg ut som:
<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.">
Varje, singel, sida, samma beskrivning, min artikel om Bakgrundstjänster i ASP.NET Core? Samma beskrivning som min hemsida. Min djupdykning i RAG-arkitektur- Samma beskrivning.
Google ser 200+ sidor med identiska beskrivningar och tänker "den här webbplatsen har duplicerade innehållsproblem" eller "den här webbplatsen bryr sig inte om att ge användbar information." Oavsett, ranking lider.
Jag har en flerspråkig blogg med översättningar. Samma innehåll finns på:
/blog/my-article (engelska)/blog/my-article/fr (Franska)/blog/my-article/de (tyska)Utan kanoniska webbadresser kan Google se dessa som duplicerat innehåll och späda ut SEO-värdet över flera webbadresser istället för att konsolidera det på den primära sidan.
Googles sökresultat kan visa rika snippets - författare information, publicera datum, artikel förhandsvisningar. Men bara om du berättar för Google om denna data med hjälp av Strukturerade JSON-LD-uppgifter- Det gjorde jag inte.
Varje sida delade samma og:image. Även om detta inte direkt påverkar Google ranking, det påverkar klick-genom-hastigheter när artiklar delas på sociala medier - som indirekt påverkar SEO genom engagemang signaler.
Den första fixen var att göra layouten stöd dynamiska beskrivningar med en vettig reserv:
@{
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" />
}
}
Nu läser layouten ViewBag.Description Om inställd, återgår till en standard. Den kanoniska webbadressen ställs automatiskt in till den aktuella sidans webbadress.
Varje blogginlägg genererar nu sin beskrivning från de första 155 tecken på innehåll. 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}";
}
De viktigaste punkterna här:
og:type till "artikel" - Berättar sociala plattformar detta är en artikel, inte en allmän hemsidaDetta är den stora för rika snippets. Lägg till en JSON-LD skriptblock till din blogg inläggsvy:
<!-- 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>
Lägg märke till @@ fly för @ symbol i Razor-vyer - JSON-LD användningar @context och @type som annars skulle tolkas som Razor syntax.
Denna strukturerade data berättar för Google:
Google kan använda detta för att visa rika snippets i sökresultat, inklusive författarinformation, publicera datum och mycket mer.
Glöm inte dina statiska sidor. Var och en ska ha en unik, relevant beskrivning:
<!-- 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.";
}
Du behöver en sitemap. Här är en enkel controller som genererar en dynamisk:
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");
}
}
Registrera rutten:
app.MapControllerRoute(
name: "sitemap",
pattern: "sitemap.xml",
defaults: new { controller = "SiteMap", action = "Index" });
Berätta för sökmotorer var din sitemap är och vad du ska krypa:
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);
});
Många utvecklare använder RSS-läsare. Att ha ett RSS-flöde hjälper också till att upptäcka:
<link rel="alternate" type="application/atom+xml"
title="RSS Feed for mostlylucid.net"
href="https://mostlylucid.net/rss" />
Du kanske undrar om generera unika OG bilder för varje inlägg. För en teknisk blogg, är det förmodligen inte värt ansträngningen. Din trafik kommer från:
En konsekvent märkesbild är bra för igenkänning. Anpassad bildgenerering betyder mer för visuellt innehåll, nyhetssidor eller marknadsföringssidor som konkurrerar om klick på Facebook och Twitter.
Om du ville generera dem, kunde du använda AvbildaSharp (som jag redan använder för bildbehandling) för att lägga över text på en mall:
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";
}
Men för en teknisk blogg? Ship vad du har.
Användning Googles rika resultat test för att validera din strukturerade data. Klistra in en URL och det kommer att berätta om din JSON-LD är giltig och vilka rika resultat du är berättigad till.
Det enklaste testet - visa sidans källa och kontrollera:
<meta name="description"> unik för denna sida?<link rel="canonical">?<script type="application/ld+json"> Blockera?Efter utplacering, skicka in din webbplatskarta till Google Sökkonsol. Du kan också använda URL Inspection tool för att se exakt hur Google ser dina sidor och begära omindexering.
Rättelserna var enkla:
Problemet är att rätta till det.
|---------|-----|
till Statiska metabeskrivningar på Dynamiska ViewBag.Description med automatisk generering från innehåll
Inga kanoniska webbadresser tillagda <link rel="canonical"> till layout
Inga strukturerade data på JSON-LD BlogPosting schema på varje post till
till Saknar artikelmetadata article:published_time, article:author, article:tag metataggar
till Statiska sidbeskrivningar och unika beskrivningar för Hem, Blogg, Kontakt, Söksidor
Den viktigaste insikten: Google kan inte läsa dina tankar. Om varje sida har samma beskrivning, Google har inget sätt att veta vad som gör varje sida unik och värdefull. Berätta för Google vad varje sida handlar om, och du kommer ranka bättre.
Ge det några veckor för Google att re-crawl din webbplats, och du bör se förbättringar. SEO är ett långt spel, men dessa grundläggande är grunden allt bygger på.
© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.