SEO Performance optimieren für Unternehmen in Dortmund: Core Web Vitals, PageSpeed Insights & Screaming Frog

1) Performance-SEO in Dortmund systematisch aufsetzen (Messmodell → Maßnahmen → Wirkung)
Schritt 1: Zielmetriken festlegen (Core Web Vitals + Business-KPIs)
Für lokale Unternehmen in Dortmund ist Performance-SEO dann sinnvoll, wenn es direkt auf Conversions einzahlt (Anfragen, Calls, Terminbuchungen). Technisch übersetzen wir das in ein Messmodell aus Core Web Vitals und SEO-Kennzahlen:
Interessiert an diesem Thema?
Kontaktieren Sie uns für eine kostenlose Beratung →- LCP (Largest Contentful Paint): Ziel i.d.R. ≤ 2,5s
- INP (Interaction to Next Paint): Ziel i.d.R. ≤ 200ms
- CLS (Cumulative Layout Shift): Ziel i.d.R. ≤ 0,1
- Page Authority: indirekte Erfolgskennzahl (stärker durch Content/Links, aber Performance beeinflusst Crawl/UX)
- Keyword Density: Qualitäts-Guardrail (nicht „hochdrehen“, sondern natürlich + entitätenbasiert steuern)
Wichtig: Core Web Vitals sind sowohl UX- als auch Ranking-Signale. In umkämpften lokalen SERPs (z.B. „Steuerberater Dortmund“, „Zahnarzt Dortmund“, „Bauunternehmen Dortmund“) kann Performance den Ausschlag geben, wenn Inhalte vergleichbar sind.
Schritt 2: Priorisieren nach Seitentyp (Money Pages zuerst)
Für den Target-Cluster rund um SEO Agentur priorisieren Sie:
- Landingpages (Leistung + Standortbezug Dortmund)
- Kontakt/Termin (Conversion-Pfade)
- Ratgeber/Blog (Traffic-Aufbau, interne Verlinkung)
2) PageSpeed Insights richtig nutzen (Daten lesen, nicht nur Score jagen)
Schritt 1: Lab vs. Field Data verstehen
PageSpeed Insights zeigt zwei Datenwelten:
- Field Data (CrUX): echte Nutzerdaten, direkt relevant für Core Web Vitals
- Lab Data (Lighthouse): synthetisch, ideal zum Debuggen
In Dortmund-relevanten Zielgruppen (mobile Nutzer, wechselnde Netzqualität) ist es üblich, dass Lab „gut“ aussieht, Field aber schwankt. Optimieren Sie daher auf stabile Field-Werte: weniger JS, effizientere Bilder, schnelleres Rendering.
Schritt 2: Typische Performance-Bremsen (und schnelle Wins)
- Render-blocking CSS/JS → Critical CSS, Defer/Async, Code-Splitting
- Zu große Hero-Bilder → AVIF/WebP, korrektes Sizing, Preload für LCP-Element
- Drittanbieter-Skripte (Chat, Tracking) → Consent-gesteuert nachladen, Tag-Reduktion
- Fonts → self-host, preload, font-display: swap
Schritt 3: INP gezielt verbessern (nicht nur „weniger JS“, sondern „weniger Main-Thread“)
INP leidet oft an langen Tasks durch Frameworks, Tag-Manager und unoptimierte Event-Handler. Maßnahmen:
- Interaktionen identifizieren (Menü, Filter, Formular)
- Long Tasks in kleinere Chunks splitten
- Third-Party Scripts reduzieren und später laden
3) Screaming Frog Analysis: Technisches SEO-Audit für Performance & Indexierbarkeit
Schritt 1: Crawl-Setup für Dortmund-SEO
Eine saubere Screaming Frog Analysis verbindet Technik + Inhalte. Crawl-Ziele:
- Indexierbare Seiten vs. Noindex/Canonical-Konflikte
- Response Codes (3xx/4xx/5xx) und Redirect-Ketten
- Duplicate Titles/Meta Descriptions
- Interne Linktiefe (wichtige Seiten zu tief?)
- Thin Content und Keyword-Kannibalisierung (z.B. mehrere „Dortmund“-Landingpages mit ähnlichem Fokus)
Schritt 2: Performance-Signale aus Crawl-Daten ableiten
Auch ohne echte Core-Web-Vitals-Messung liefert ein Crawl starke Hinweise:
- Große HTML-Seiten (DOM-Bloat) → oft INP/CLS-Risiko
- Viele Skripte/Requests → häufig LCP/INP-Probleme
- Bild-URLs ohne moderne Formate → LCP-Quick-Win
Schritt 3: Page Authority über interne Architektur hebeln
Page Authority steigt nicht nur durch externe Links, sondern auch durch eine klare interne Linkstruktur:
- Starke Themencluster (z.B. „SEO Dortmund“ → Unterseiten für Technik, Content, Local SEO)
- Interne Links von Traffic-Seiten auf Money Pages (z.B. /seo-agentur)
- Breadcrumbs & Kontextlinks (semantisch, nicht nur Footer)
4) Keyword Density datenbasiert steuern (ohne Over-Optimization)
Schritt 1: Keyword-Set definieren (Hauptkeyword + Entitäten)
Für Dortmund kombinieren Sie:
- Transaktional: „SEO Agentur Dortmund“, „SEO Beratung Dortmund“
- Informational: „Core Web Vitals verbessern“, „PageSpeed Insights interpretieren“
- Entitäten/Co-Occurences: „Core Web Vitals“, „PageSpeed Insights“, „Screaming Frog“, „Page Authority“, „Keyword Density“
Die Keyword Density sollte als Plausibilitätscheck dienen, nicht als Zielwert. Entscheidend ist thematische Abdeckung (Suchintention + Entitäten + Beispiele).
Schritt 2: Python Script for Keyword Analysis
Dieses Script crawlt eine URL-Liste (z.B. Seiten aus Screaming Frog exportiert), extrahiert Text, berechnet Keyword Density für ein Set von Begriffen und gibt eine CSV aus.
import csv
import re
import time
from collections import Counter
from dataclasses import dataclass
from typing import Dict, List, Tuple
import requests
from bs4 import BeautifulSoup
@dataclass
class PageResult:
url: str
word_count: int
densities: Dict[str, float]
top_terms: List[Tuple[str, int]]
def normalize_text(text: str) -> str:
text = text.lower()
text = re.sub(r"[^a-z0-9äöüß\s-]", " ", text)
text = re.sub(r"\s+", " ", text).strip()
return text
def extract_visible_text(html: str) -> str:
soup = BeautifulSoup(html, "html.parser")
for tag in soup(["script", "style", "noscript", "svg", "header", "footer", "nav"]):
tag.decompose()
text = soup.get_text(separator=" ")
return normalize_text(text)
def count_terms(text: str) -> Counter:
tokens = [t for t in text.split(" ") if len(t) > 1]
return Counter(tokens)
def keyword_density(text: str, keyword: str) -> float:
tokens = text.split(" ")
if not tokens:
return 0.0
# Keyword kann aus mehreren Wörtern bestehen
kw = normalize_text(keyword)
kw_tokens = kw.split(" ")
if not kw_tokens:
return 0.0
hits = 0
for i in range(0, len(tokens) - len(kw_tokens) + 1):
if tokens[i : i + len(kw_tokens)] == kw_tokens:
hits += 1
return (hits / len(tokens)) * 100.0
def analyze_page(url: str, keywords: List[str], timeout: int = 20) -> PageResult:
r = requests.get(
url,
timeout=timeout,
headers={"User-Agent": "KeywordDensityBot/1.0 (+internal SEO audit)"},
)
r.raise_for_status()
text = extract_visible_text(r.text)
tokens = [t for t in text.split(" ") if t]
counts = count_terms(text)
densities = {kw: round(keyword_density(text, kw), 4) for kw in keywords}
top_terms = counts.most_common(15)
return PageResult(
url=url,
word_count=len(tokens),
densities=densities,
top_terms=top_terms,
)
def main() -> None:
input_file = "urls.txt" # eine URL pro Zeile (z.B. Export aus Screaming Frog)
output_file = "keyword_density_report.csv"
keywords = [
"seo agentur",
"dortmund",
"core web vitals",
"pagespeed insights",
"screaming frog",
"page authority",
"keyword density",
]
with open(input_file, "r", encoding="utf-8") as f:
urls = [line.strip() for line in f if line.strip() and not line.startswith("#")]
rows = []
for url in urls:
try:
result = analyze_page(url, keywords)
row = {
"url": result.url,
"word_count": result.word_count,
}
for kw, dens in result.densities.items():
row[f"density_{kw}"] = dens
row["top_terms"] = "; ".join([f"{t}:{c}" for t, c in result.top_terms])
rows.append(row)
time.sleep(0.5)
except Exception as e:
rows.append({"url": url, "word_count": 0, "error": str(e)})
fieldnames = sorted({k for r in rows for k in r.keys()})
with open(output_file, "w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=fieldnames)
writer.writeheader()
writer.writerows(rows)
if __name__ == "__main__":
main()
5) Umsetzung: Performance-Backlog (Dortmund-SEO) als klare Roadmap
| Feature | Details |
|---|---|
| Core Web Vitals Fixes | LCP über Hero-Optimierung (Preload + AVIF), INP durch JS-Reduktion/Chunking, CLS durch feste Größen für Medien/Ads |
| PageSpeed Insights Workflow | Field Data priorisieren, Lab Findings in Tickets übersetzen, vor/nach Messung pro Template |
| Screaming Frog Analysis | Indexability, Redirects, Duplicate Metas, Linktiefe, Thin Content, Kannibalisierung; Export als Basis für Fix-Listen |
| Page Authority Hebel | Interne Verlinkung (Cluster), thematische Hubs, saubere Canonicals, konsistente URL-Struktur |
| Keyword Density Guardrails | Automatisierter Report pro Landingpage, Fokus auf Entitäten statt Wiederholungen, Snippet-Optimierung ohne Stuffing |
6) JSON Config for SEO Tools (inkl. TypeScript-Runner)
Schritt 1: Konfiguration als JSON (mit \n-Zeilenumbrüchen)
Dieses JSON kann als zentrale Konfig für Ihr Audit dienen (Keywords, URLs, Schwellenwerte für Core Web Vitals, Exporte). Hinweis: Die JSON-Strings enthalten explizit \n.
{
"project": {
"name": "Dortmund SEO Performance Audit",
"targetUrl": "/seo-agentur",
"primaryAnchor": "SEO Agentur",
"location": "Dortmund"
},
"thresholds": {
"coreWebVitals": {
"lcpSeconds": 2.5,
"inpMilliseconds": 200,
"cls": 0.1
},
"keywordDensity": {
"warningAbovePercent": 3.0,
"minimumWords": 250
}
},
"keywords": [
"SEO Agentur Dortmund",
"Core Web Vitals",
"PageSpeed Insights",
"Screaming Frog Analysis",
"Page Authority",
"Keyword Density"
],
"inputs": {
"urlListFile": "urls.txt",
"screamingFrogExports": {
"internalHtmlCsv": "screamingfrog_internal_html.csv",
"responseCodesCsv": "screamingfrog_response_codes.csv"
}
},
"outputs": {
"keywordReportCsv": "keyword_density_report.csv",
"summaryMarkdown": "audit_summary.md"
},
"notes": "Runbook:\n1) Export URLs from Screaming Frog\n2) Run Python keyword density script\n3) Validate PSI (Field + Lab) per template\n4) Prioritize fixes by conversion impact\n"
}
Schritt 2: TypeScript Runner, der die JSON-Config lädt und Checks ausführt
Das folgende TypeScript-Beispiel liest die Config, validiert Basiswerte und erstellt eine simple Audit-Zusammenfassung (als Ausgangspunkt für Automatisierung).
import { readFile, writeFile } from "node:fs/promises";
type CoreWebVitalsThresholds = {
lcpSeconds: number;
inpMilliseconds: number;
cls: number;
};
type Config = {
project: {
name: string;
targetUrl: string;
primaryAnchor: string;
location: string;
};
thresholds: {
coreWebVitals: CoreWebVitalsThresholds;
keywordDensity: {
warningAbovePercent: number;
minimumWords: number;
};
};
keywords: string[];
inputs: {
urlListFile: string;
screamingFrogExports: {
internalHtmlCsv: string;
responseCodesCsv: string;
};
};
outputs: {
keywordReportCsv: string;
summaryMarkdown: string;
};
notes?: string;
};
function validateConfig(cfg: Config): string[] {
const issues: string[] = [];
if (!cfg.project.targetUrl.startsWith("/")) {
issues.push("project.targetUrl should be a path starting with '/'.");
}
const cwv = cfg.thresholds.coreWebVitals;
if (cwv.lcpSeconds <= 0 || cwv.inpMilliseconds <= 0 || cwv.cls <= 0) {
issues.push("Core Web Vitals thresholds must be positive numbers.");
}
if (cfg.keywords.length === 0) {
issues.push("keywords must not be empty.");
}
return issues;
}
async function main() {
const raw = await readFile("seo-tools.config.json", "utf-8");
const cfg = JSON.parse(raw) as Config;
const issues = validateConfig(cfg);
const lines: string[] = [];
lines.push(`# ${cfg.project.name}`);
lines.push("");
lines.push(`**Standort:** ${cfg.project.location}`);
lines.push(`**Zielseite:** ${cfg.project.targetUrl} (Anchor: ${cfg.project.primaryAnchor})`);
lines.push("");
lines.push("Schwellenwerte (Core Web Vitals)");
lines.push(`- LCP ≤ ${cfg.thresholds.coreWebVitals.lcpSeconds}s`);
lines.push(`- INP ≤ ${cfg.thresholds.coreWebVitals.inpMilliseconds}ms`);
lines.push(`- CLS ≤ ${cfg.thresholds.coreWebVitals.cls}`);
lines.push("");
lines.push("Keyword-Set");
for (const kw of cfg.keywords) {
lines.push(`- ${kw}`);
}
lines.push("");
if (issues.length) {
lines.push("Config-Issues");
for (const issue of issues) {
lines.push(`- ${issue}`);
}
lines.push("");
} else {
lines.push("Status");
lines.push("- Config looks valid. Next: run PSI checks and Screaming Frog exports.");
lines.push("");
}
if (cfg.notes) {
lines.push("Notes");
lines.push(cfg.notes);
}
await writeFile(cfg.outputs.summaryMarkdown, lines.join("\n"), "utf-8");
}
main().catch((err) => {
console.error(err);
process.exit(1);
});
7) Visual: Audit-Flow (von Crawl → PSI → Fix → Re-Check)
graph TD
A[Screaming Frog Analysis: Crawl & Exporte] --> B[Technische Findings: Indexability, Redirects, Linktiefe]
B --> C[Performance Hypothesen: LCP/INP/CLS Treiber]
C --> D[PageSpeed Insights: Field + Lab Validierung]
D --> E[Backlog: Quick Wins + Template-Fixes]
E --> F[Deploy + Monitoring: Core Web Vitals]
F --> G[SEO Impact: Rankings, CTR, Conversions]
G --> A


