Back to "Waarom je dit niet hebt gevonden op Google (En hoe ik het heb opgelost)"

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

ASP.NET Core SEO

Waarom je dit niet hebt gevonden op Google (En hoe ik het heb opgelost)

Wednesday, 26 November 2025

Ik heb deze blog draaien voor een tijdje nu, het schrijven van gedetailleerde technische artikelen over ASP.NET Core, Entity Framework, HTMX, en allerlei .NET goodness. Toch mijn zoekrankings waren... underwhelming. Na eindelijk onderzoeken waarom, Ik ontdekte dat ik mijn eigen SEO saboteren met een aantal rookie fouten. Hier is wat verkeerd was en hoe ik het opgelost.

Het probleem: elke pagina leek identiek aan Google

Wanneer Google kruipt uw site, het kijkt naar verschillende belangrijke signalen om te begrijpen waar elke pagina over gaat. Ik was het verzenden van Google dezelfde signalen voor elke pagina.

Het Rookpistool: Statische Meta Beschrijvingen

Hier is wat mijn _Layout.cshtml zag eruit als:

<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.">

Elke pagina, dezelfde beschrijving, mijn artikel over... Achtergronddiensten in ASP.NET Core♪ Dezelfde beschrijving als mijn homepage. Mijn diepe duik in RAG-architectuurDezelfde beschrijving.

Google ziet 200+ pagina's met identieke beschrijvingen en denkt "deze site heeft dubbele inhoud problemen" of "deze site geeft niet om het verstrekken van nuttige informatie." Hoe dan ook, rankings lijden.

Ontbrekende Canonische URL's

Ik heb een meertalige blog met vertalingen. Dezelfde inhoud bestaat op:

  • /blog/my-article (Nederlands)
  • /blog/my-article/fr (Frans)
  • /blog/my-article/de (Duits)

Zonder canonieke URL's zou Google deze kunnen zien als dubbele inhoud, waardoor de SEO-waarde wordt verdund over meerdere URL's in plaats van te consolideren op de primaire pagina.

Geen gestructureerde gegevens

De zoekresultaten van Google kunnen rijke knipsels tonen - auteur informatie, publiceren data, artikel previews. Maar alleen als u Google over deze gegevens te vertellen met behulp van JSON-LD gestructureerde gegevensDat was ik niet.

Statische sociale afbeeldingen

Elke pagina gedeeld hetzelfde og:image. Hoewel dit geen directe invloed heeft op Google-rankings, beïnvloedt het de click-through tarieven wanneer artikelen worden gedeeld op sociale media - die indirect invloed hebben op SEO door middel van engagement signalen.

The Fixes

1. Dynamische Meta-beschrijvingen

De eerste oplossing was het maken van de layout ondersteuning dynamische beschrijvingen met een verstandige terugval:

@{
    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 leest de lay-out ViewBag.Description indien ingesteld, terugvallend op een standaard. De canonieke URL wordt automatisch ingesteld op de huidige pagina-URL.

2. Auto-gegenereerde beschrijvingen voor Blog berichten

Elke blog post genereert nu zijn beschrijving van de eerste 155 karakters van de inhoud. 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 belangrijkste punten zijn:

  1. Afkappen bij woordgrens - We vinden de laatste ruimte voor 155 tekens om te voorkomen dat woorden in tweeën worden gesneden
  2. Strip nieuwe lijnen - Meta-beschrijvingen moeten één regel zijn
  3. Instellen og:type naar "artikel" - Vertelt sociale platforms dit is een artikel, geen generieke webpagina
  4. Artikelmetadata doorgeven - Gepubliceerde datum en categorieën stroom door naar de lay-out

3. JSON-LD gestructureerde gegevens

Dit is de grote voor rijke snippets. Voeg een JSON-LD scriptblok toe aan uw blog postweergave:

<!-- 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>

Let op de @@ ontsnapping voor de @ symbool in Razor views - JSON-LD maakt gebruik van @context en @type die anders zou worden geïnterpreteerd als Razor syntax.

Deze gestructureerde gegevens vertellen Google:

  • Wat voor soort inhoud dit is (BlogPosting)
  • Wie schreef het? (Person met een URL om meer te leren)
  • Toen het werd gepubliceerd en gewijzigd
  • Welke onderwerpen worden daarin behandeld? (trefwoorden uit categorieën)
  • Hoe lang is het? (woordCount)
  • Welke taal het is in

Google kan dit gebruiken om rijke snippets te tonen in zoekresultaten, waaronder auteur informatie, publiceren data, en nog veel meer.

4. Unieke beschrijvingen voor sleutelpagina's

Vergeet uw statische pagina's niet. Elk moet een unieke, relevante beschrijving hebben:

<!-- 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.";
}

Andere SEO Essentials

Sitemap

Je hebt een sitemap nodig. Hier is een eenvoudige controller die er een dynamisch genereert:

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");
    }
}

Registreer de route:

app.MapControllerRoute(
    name: "sitemap",
    pattern: "sitemap.xml",
    defaults: new { controller = "SiteMap", action = "Index" });

robots.txt

Vertel zoekmachines waar uw sitemap is en wat te kruipen:

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-feed

Veel ontwikkelaars gebruiken RSS-lezers. Het hebben van een RSS-feed helpt ook bij de ontdekbaarheid:

<link rel="alternate" type="application/atom+xml"
      title="RSS Feed for mostlylucid.net"
      href="https://mostlylucid.net/rss" />

Hoe zit het met afbeeldingen?

U kunt zich afvragen over het genereren van unieke OG-beelden voor elke post. Voor een technische blog, het is waarschijnlijk niet de moeite waard. Uw verkeer komt uit:

  • Google-zoekopdracht - beschrijvingen zijn belangrijker dan afbeeldingen
  • RSS-feeds - geen afbeeldingen
  • Hacker News / Reddit - miniaturen nauwelijks zichtbaar
  • Directe links - ontwikkelaars die URL's delen

Een consistent merk beeld is prima voor herkenning. Custom beeldgeneratie is belangrijker voor visuele inhoud, nieuwssites, of marketing pagina's concurreren voor klikken op Facebook en Twitter.

Als je ze wilt genereren, kun je ze gebruiken. ImageSharp (die ik al gebruik voor beeldverwerking) om tekst op een sjabloon te overlayen:

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";
}

Maar voor een technisch blog?

Uw SEO testen

Google Rich Results Test

Gebruik Google's Rich Results Test om uw gestructureerde gegevens te valideren. Plakken een URL en het zal u vertellen of uw JSON-LD geldig is en voor welke rijke resultaten u in aanmerking komt.

Paginabron tonen

De eenvoudigste test - bekijk de paginabron en controleer:

  • De Voorzitter. - Het debat is gesloten. <meta name="description"> uniek voor deze pagina?
  • Is er een <link rel="canonical">?
  • Is er een <script type="application/ld+json"> Blok?

Zoekconsole van Google

Na het implementeren, stuur uw sitemap naar Zoekconsole van Google. U kunt ook gebruik maken van de URL Inspectie tool om precies te zien hoe Google ziet uw pagina's en verzoeken opnieuw te indexeren.

Samenvatting

De fixes waren eenvoudig:

Probleem Fix |---------|-----| Statische meta-beschrijvingen Dynamisch ViewBag.Description met auto-generatie van inhoud Geen canonieke URL's toegevoegd <link rel="canonical"> naar lay-out Geen gestructureerde gegevens JSON-LD BlogPosting schema voor elke post . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ontbrekende artikel metadata article:published_time, article:author, article:tag meta-tags Unieke beschrijvingen voor Home, Blog, Contact, Zoekpagina's

Het belangrijkste inzicht: Google kan je gedachten niet lezen. Als elke pagina dezelfde beschrijving heeft, heeft Google geen manier om te weten wat maakt elke pagina uniek en waardevol. Vertel Google waar elke pagina over gaat, en je zult beter rangschikken.

Geef het een paar weken voor Google om opnieuw te kruipen uw site, en je moet verbeteringen zien. SEO is een lang spel, maar deze fundamentele beginselen zijn de basis alles bouwt op.

Meer lezen

logo

© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.