Initial commit: Portfolio Website
Vollständige Next.js 15 Portfolio-Website mit: - Blog-System mit 100+ Artikeln - Supabase-Integration - Responsive Design mit Tailwind CSS - TypeScript-Konfiguration - Testing-Setup mit Vitest und Playwright Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,584 @@
|
||||
# Technical SEO Checklist
|
||||
|
||||
**Meta-Description:** Komplette Technical SEO Checklist für 2026. Crawling, Indexierung, Site Structure und Performance-Optimierung für bessere Rankings.
|
||||
|
||||
**Keywords:** Technical SEO, SEO Checklist, Crawling, Indexierung, Site Structure, Page Speed, Mobile SEO, SEO Audit
|
||||
|
||||
---
|
||||
|
||||
## Einführung
|
||||
|
||||
**Technical SEO** bildet das Fundament für erfolgreiche Suchmaschinenoptimierung. Ohne saubere technische Basis nützt der beste Content wenig. Diese Checklist deckt alle wichtigen Aspekte ab – von **Crawling** bis **Performance**.
|
||||
|
||||
---
|
||||
|
||||
## Technical SEO Overview
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ TECHNICAL SEO PYRAMID │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌─────────┐ │
|
||||
│ │ Content │ │
|
||||
│ │ & UX │ │
|
||||
│ └────┬────┘ │
|
||||
│ ┌─────────┴─────────┐ │
|
||||
│ │ On-Page SEO │ │
|
||||
│ │ (Title, Meta,H1) │ │
|
||||
│ └─────────┬─────────┘ │
|
||||
│ ┌──────────────┴──────────────┐ │
|
||||
│ │ Site Architecture │ │
|
||||
│ │ (URLs, Internal Links) │ │
|
||||
│ └──────────────┬──────────────┘ │
|
||||
│ ┌───────────────────┴───────────────────┐ │
|
||||
│ │ Crawling & Indexing │ │
|
||||
│ │ (robots.txt, sitemap, canonicals) │ │
|
||||
│ └───────────────────┬───────────────────┘ │
|
||||
│ ┌──────────────────────┴──────────────────────┐ │
|
||||
│ │ Server & Performance │ │
|
||||
│ │ (HTTPS, Speed, Core Web Vitals, Mobile) │ │
|
||||
│ └─────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ Priority Levels: │
|
||||
│ 🔴 Critical - Must fix immediately │
|
||||
│ 🟡 Important - Fix soon │
|
||||
│ 🟢 Nice to have - Optimize when possible │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Server & Security
|
||||
|
||||
```typescript
|
||||
// lib/seo/audit/server.ts
|
||||
interface ServerAuditResult {
|
||||
https: boolean;
|
||||
httpToHttpsRedirect: boolean;
|
||||
wwwRedirect: 'www' | 'non-www' | 'inconsistent';
|
||||
responseTime: number;
|
||||
serverHeaders: Record<string, string>;
|
||||
issues: string[];
|
||||
}
|
||||
|
||||
async function auditServer(url: string): Promise<ServerAuditResult> {
|
||||
const issues: string[] = [];
|
||||
|
||||
// HTTPS Check
|
||||
const https = url.startsWith('https://');
|
||||
if (!https) {
|
||||
issues.push('🔴 Site is not using HTTPS');
|
||||
}
|
||||
|
||||
// HTTP to HTTPS Redirect
|
||||
const httpUrl = url.replace('https://', 'http://');
|
||||
const httpResponse = await fetch(httpUrl, { redirect: 'manual' });
|
||||
const httpToHttpsRedirect = httpResponse.status === 301 &&
|
||||
httpResponse.headers.get('location')?.startsWith('https://');
|
||||
|
||||
if (!httpToHttpsRedirect) {
|
||||
issues.push('🔴 HTTP does not redirect to HTTPS');
|
||||
}
|
||||
|
||||
// WWW Consistency
|
||||
const withWww = url.includes('www.');
|
||||
const testUrl = withWww
|
||||
? url.replace('www.', '')
|
||||
: url.replace('://', '://www.');
|
||||
|
||||
const wwwResponse = await fetch(testUrl, { redirect: 'manual' });
|
||||
const wwwRedirect = wwwResponse.status === 301 ? (withWww ? 'www' : 'non-www') : 'inconsistent';
|
||||
|
||||
if (wwwRedirect === 'inconsistent') {
|
||||
issues.push('🟡 WWW and non-WWW versions not properly redirected');
|
||||
}
|
||||
|
||||
// Response Time
|
||||
const start = Date.now();
|
||||
await fetch(url);
|
||||
const responseTime = Date.now() - start;
|
||||
|
||||
if (responseTime > 500) {
|
||||
issues.push(`🟡 Server response time is slow: ${responseTime}ms`);
|
||||
}
|
||||
|
||||
// Security Headers
|
||||
const response = await fetch(url);
|
||||
const serverHeaders: Record<string, string> = {};
|
||||
response.headers.forEach((value, key) => {
|
||||
serverHeaders[key] = value;
|
||||
});
|
||||
|
||||
const requiredHeaders = [
|
||||
'strict-transport-security',
|
||||
'x-content-type-options',
|
||||
'x-frame-options'
|
||||
];
|
||||
|
||||
requiredHeaders.forEach(header => {
|
||||
if (!serverHeaders[header]) {
|
||||
issues.push(`🟢 Missing security header: ${header}`);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
https,
|
||||
httpToHttpsRedirect,
|
||||
wwwRedirect,
|
||||
responseTime,
|
||||
serverHeaders,
|
||||
issues
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
```typescript
|
||||
// middleware.ts - Security Headers
|
||||
import { NextResponse } from 'next/server';
|
||||
import type { NextRequest } from 'next/server';
|
||||
|
||||
export function middleware(request: NextRequest) {
|
||||
const response = NextResponse.next();
|
||||
|
||||
// Security Headers
|
||||
response.headers.set('X-DNS-Prefetch-Control', 'on');
|
||||
response.headers.set('Strict-Transport-Security', 'max-age=63072000; includeSubDomains; preload');
|
||||
response.headers.set('X-Content-Type-Options', 'nosniff');
|
||||
response.headers.set('X-Frame-Options', 'SAMEORIGIN');
|
||||
response.headers.set('X-XSS-Protection', '1; mode=block');
|
||||
response.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin');
|
||||
|
||||
// Permissions Policy
|
||||
response.headers.set(
|
||||
'Permissions-Policy',
|
||||
'camera=(), microphone=(), geolocation=(self), interest-cohort=()'
|
||||
);
|
||||
|
||||
return response;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Crawling & Indexing
|
||||
|
||||
```typescript
|
||||
// app/robots.ts - Optimized robots.txt
|
||||
import { MetadataRoute } from 'next';
|
||||
|
||||
export default function robots(): MetadataRoute.Robots {
|
||||
const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://example.com';
|
||||
|
||||
return {
|
||||
rules: [
|
||||
{
|
||||
userAgent: '*',
|
||||
allow: '/',
|
||||
disallow: [
|
||||
'/api/',
|
||||
'/admin/',
|
||||
'/_next/',
|
||||
'/private/',
|
||||
'/*.json$',
|
||||
'/*?*', // Query Parameter
|
||||
'/search?*'
|
||||
]
|
||||
},
|
||||
{
|
||||
userAgent: 'Googlebot',
|
||||
allow: '/',
|
||||
disallow: ['/admin/', '/api/']
|
||||
},
|
||||
{
|
||||
// Block bad bots
|
||||
userAgent: ['AhrefsBot', 'SemrushBot', 'DotBot'],
|
||||
disallow: '/'
|
||||
}
|
||||
],
|
||||
sitemap: `${baseUrl}/sitemap.xml`,
|
||||
host: baseUrl
|
||||
};
|
||||
}
|
||||
|
||||
// app/sitemap.ts - Dynamic Sitemap
|
||||
import { MetadataRoute } from 'next';
|
||||
import { getAllPages, getAllPosts, getAllProducts } from '@/lib/content';
|
||||
|
||||
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
|
||||
const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://example.com';
|
||||
|
||||
// Static Pages
|
||||
const staticPages: MetadataRoute.Sitemap = [
|
||||
{
|
||||
url: baseUrl,
|
||||
lastModified: new Date(),
|
||||
changeFrequency: 'daily',
|
||||
priority: 1.0
|
||||
},
|
||||
{
|
||||
url: `${baseUrl}/about`,
|
||||
lastModified: new Date(),
|
||||
changeFrequency: 'monthly',
|
||||
priority: 0.8
|
||||
},
|
||||
{
|
||||
url: `${baseUrl}/contact`,
|
||||
lastModified: new Date(),
|
||||
changeFrequency: 'yearly',
|
||||
priority: 0.5
|
||||
},
|
||||
{
|
||||
url: `${baseUrl}/blog`,
|
||||
lastModified: new Date(),
|
||||
changeFrequency: 'daily',
|
||||
priority: 0.9
|
||||
}
|
||||
];
|
||||
|
||||
// Dynamic Pages
|
||||
const pages = await getAllPages();
|
||||
const dynamicPages: MetadataRoute.Sitemap = pages.map(page => ({
|
||||
url: `${baseUrl}/${page.slug}`,
|
||||
lastModified: new Date(page.updatedAt),
|
||||
changeFrequency: 'monthly',
|
||||
priority: 0.7
|
||||
}));
|
||||
|
||||
// Blog Posts
|
||||
const posts = await getAllPosts();
|
||||
const blogPosts: MetadataRoute.Sitemap = posts.map(post => ({
|
||||
url: `${baseUrl}/blog/${post.slug}`,
|
||||
lastModified: new Date(post.updatedAt),
|
||||
changeFrequency: 'weekly',
|
||||
priority: 0.8
|
||||
}));
|
||||
|
||||
// Products
|
||||
const products = await getAllProducts();
|
||||
const productPages: MetadataRoute.Sitemap = products.map(product => ({
|
||||
url: `${baseUrl}/products/${product.slug}`,
|
||||
lastModified: new Date(product.updatedAt),
|
||||
changeFrequency: 'daily',
|
||||
priority: 0.9
|
||||
}));
|
||||
|
||||
return [...staticPages, ...dynamicPages, ...blogPosts, ...productPages];
|
||||
}
|
||||
```
|
||||
|
||||
```typescript
|
||||
// lib/seo/canonical.ts - Canonical URL Helper
|
||||
export function getCanonicalUrl(path: string, locale?: string): string {
|
||||
const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://example.com';
|
||||
|
||||
// Remove trailing slash
|
||||
const cleanPath = path.replace(/\/$/, '');
|
||||
|
||||
// Remove query parameters for canonical
|
||||
const pathWithoutQuery = cleanPath.split('?')[0];
|
||||
|
||||
// Add locale if needed
|
||||
if (locale && locale !== 'de') {
|
||||
return `${baseUrl}/${locale}${pathWithoutQuery}`;
|
||||
}
|
||||
|
||||
return `${baseUrl}${pathWithoutQuery}`;
|
||||
}
|
||||
|
||||
// Usage in page
|
||||
export async function generateMetadata({ params }: PageProps): Promise<Metadata> {
|
||||
return {
|
||||
alternates: {
|
||||
canonical: getCanonicalUrl(`/blog/${params.slug}`)
|
||||
}
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## URL Structure
|
||||
|
||||
```typescript
|
||||
// lib/seo/url-utils.ts
|
||||
export function createSEOFriendlyUrl(title: string): string {
|
||||
return title
|
||||
.toLowerCase()
|
||||
.trim()
|
||||
// Umlaute ersetzen
|
||||
.replace(/ä/g, 'ae')
|
||||
.replace(/ö/g, 'oe')
|
||||
.replace(/ü/g, 'ue')
|
||||
.replace(/ß/g, 'ss')
|
||||
// Sonderzeichen entfernen
|
||||
.replace(/[^a-z0-9\s-]/g, '')
|
||||
// Mehrere Leerzeichen zu einem
|
||||
.replace(/\s+/g, ' ')
|
||||
// Leerzeichen zu Bindestrich
|
||||
.replace(/\s/g, '-')
|
||||
// Mehrere Bindestriche zu einem
|
||||
.replace(/-+/g, '-')
|
||||
// Max Länge
|
||||
.substring(0, 60);
|
||||
}
|
||||
|
||||
// URL Audit
|
||||
interface URLAuditResult {
|
||||
url: string;
|
||||
issues: string[];
|
||||
suggestions: string[];
|
||||
}
|
||||
|
||||
export function auditURL(url: string): URLAuditResult {
|
||||
const issues: string[] = [];
|
||||
const suggestions: string[] = [];
|
||||
|
||||
// Length check
|
||||
if (url.length > 75) {
|
||||
issues.push('🟡 URL is too long (> 75 characters)');
|
||||
}
|
||||
|
||||
// Uppercase check
|
||||
if (url !== url.toLowerCase()) {
|
||||
issues.push('🟡 URL contains uppercase characters');
|
||||
}
|
||||
|
||||
// Underscore check
|
||||
if (url.includes('_')) {
|
||||
issues.push('🟡 URL contains underscores (use hyphens instead)');
|
||||
}
|
||||
|
||||
// Double slashes
|
||||
if (url.includes('//') && !url.startsWith('http')) {
|
||||
issues.push('🔴 URL contains double slashes');
|
||||
}
|
||||
|
||||
// Query parameters
|
||||
if (url.includes('?')) {
|
||||
suggestions.push('Consider using clean URLs without query parameters');
|
||||
}
|
||||
|
||||
// Trailing slash consistency
|
||||
if (!url.endsWith('/') && !url.includes('.')) {
|
||||
suggestions.push('Consider adding trailing slash for consistency');
|
||||
}
|
||||
|
||||
return { url, issues, suggestions };
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## On-Page SEO
|
||||
|
||||
```typescript
|
||||
// lib/seo/audit/onpage.ts
|
||||
interface OnPageAuditResult {
|
||||
title: { value: string; length: number; issues: string[] };
|
||||
metaDescription: { value: string; length: number; issues: string[] };
|
||||
h1: { count: number; value: string; issues: string[] };
|
||||
headings: { h2: number; h3: number; h4: number; issues: string[] };
|
||||
images: { total: number; withoutAlt: number; issues: string[] };
|
||||
links: { internal: number; external: number; broken: number; issues: string[] };
|
||||
}
|
||||
|
||||
async function auditOnPage(html: string, url: string): Promise<OnPageAuditResult> {
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(html, 'text/html');
|
||||
|
||||
// Title
|
||||
const title = doc.querySelector('title')?.textContent || '';
|
||||
const titleIssues: string[] = [];
|
||||
if (!title) titleIssues.push('🔴 Missing title tag');
|
||||
else if (title.length < 30) titleIssues.push('🟡 Title too short (< 30 chars)');
|
||||
else if (title.length > 60) titleIssues.push('🟡 Title too long (> 60 chars)');
|
||||
|
||||
// Meta Description
|
||||
const metaDesc = doc.querySelector('meta[name="description"]')?.getAttribute('content') || '';
|
||||
const metaDescIssues: string[] = [];
|
||||
if (!metaDesc) metaDescIssues.push('🔴 Missing meta description');
|
||||
else if (metaDesc.length < 120) metaDescIssues.push('🟡 Meta description too short');
|
||||
else if (metaDesc.length > 160) metaDescIssues.push('🟡 Meta description too long');
|
||||
|
||||
// H1
|
||||
const h1Elements = doc.querySelectorAll('h1');
|
||||
const h1Issues: string[] = [];
|
||||
if (h1Elements.length === 0) h1Issues.push('🔴 Missing H1 tag');
|
||||
else if (h1Elements.length > 1) h1Issues.push('🟡 Multiple H1 tags found');
|
||||
|
||||
// Heading Structure
|
||||
const h2Count = doc.querySelectorAll('h2').length;
|
||||
const h3Count = doc.querySelectorAll('h3').length;
|
||||
const h4Count = doc.querySelectorAll('h4').length;
|
||||
const headingIssues: string[] = [];
|
||||
if (h2Count === 0) headingIssues.push('🟡 No H2 headings found');
|
||||
|
||||
// Images
|
||||
const images = doc.querySelectorAll('img');
|
||||
const imagesWithoutAlt = Array.from(images).filter(
|
||||
img => !img.getAttribute('alt')
|
||||
).length;
|
||||
const imageIssues: string[] = [];
|
||||
if (imagesWithoutAlt > 0) {
|
||||
imageIssues.push(`🟡 ${imagesWithoutAlt} images missing alt text`);
|
||||
}
|
||||
|
||||
// Links
|
||||
const links = doc.querySelectorAll('a[href]');
|
||||
const baseUrl = new URL(url);
|
||||
let internalLinks = 0;
|
||||
let externalLinks = 0;
|
||||
const linkIssues: string[] = [];
|
||||
|
||||
links.forEach(link => {
|
||||
const href = link.getAttribute('href') || '';
|
||||
if (href.startsWith('/') || href.includes(baseUrl.hostname)) {
|
||||
internalLinks++;
|
||||
} else if (href.startsWith('http')) {
|
||||
externalLinks++;
|
||||
|
||||
// Check for nofollow on external links
|
||||
const rel = link.getAttribute('rel') || '';
|
||||
if (!rel.includes('nofollow') && !rel.includes('sponsored')) {
|
||||
// Suggestion, not issue
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
title: { value: title, length: title.length, issues: titleIssues },
|
||||
metaDescription: { value: metaDesc, length: metaDesc.length, issues: metaDescIssues },
|
||||
h1: { count: h1Elements.length, value: h1Elements[0]?.textContent || '', issues: h1Issues },
|
||||
headings: { h2: h2Count, h3: h3Count, h4: h4Count, issues: headingIssues },
|
||||
images: { total: images.length, withoutAlt: imagesWithoutAlt, issues: imageIssues },
|
||||
links: { internal: internalLinks, external: externalLinks, broken: 0, issues: linkIssues }
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Complete SEO Checklist
|
||||
|
||||
```typescript
|
||||
// lib/seo/checklist.ts
|
||||
export const technicalSEOChecklist = {
|
||||
server: [
|
||||
{ id: 'https', priority: 'critical', item: 'HTTPS enabled', automated: true },
|
||||
{ id: 'http-redirect', priority: 'critical', item: 'HTTP redirects to HTTPS (301)', automated: true },
|
||||
{ id: 'www-redirect', priority: 'important', item: 'WWW/non-WWW consistency', automated: true },
|
||||
{ id: 'response-time', priority: 'important', item: 'Server response < 500ms', automated: true },
|
||||
{ id: 'security-headers', priority: 'nice', item: 'Security headers configured', automated: true }
|
||||
],
|
||||
|
||||
crawling: [
|
||||
{ id: 'robots-txt', priority: 'critical', item: 'robots.txt exists and valid', automated: true },
|
||||
{ id: 'sitemap', priority: 'critical', item: 'XML Sitemap exists', automated: true },
|
||||
{ id: 'sitemap-submitted', priority: 'important', item: 'Sitemap submitted to Search Console', automated: false },
|
||||
{ id: 'no-crawl-errors', priority: 'critical', item: 'No crawl errors in Search Console', automated: false },
|
||||
{ id: 'canonical-tags', priority: 'critical', item: 'Canonical tags on all pages', automated: true }
|
||||
],
|
||||
|
||||
indexing: [
|
||||
{ id: 'indexable', priority: 'critical', item: 'Important pages are indexable', automated: true },
|
||||
{ id: 'noindex-correct', priority: 'important', item: 'noindex only on non-important pages', automated: true },
|
||||
{ id: 'no-duplicate-content', priority: 'critical', item: 'No duplicate content issues', automated: true },
|
||||
{ id: 'hreflang', priority: 'important', item: 'hreflang tags for multilingual sites', automated: true }
|
||||
],
|
||||
|
||||
urlStructure: [
|
||||
{ id: 'url-readable', priority: 'important', item: 'URLs are human-readable', automated: true },
|
||||
{ id: 'url-lowercase', priority: 'important', item: 'URLs are lowercase', automated: true },
|
||||
{ id: 'url-hyphens', priority: 'nice', item: 'URLs use hyphens (not underscores)', automated: true },
|
||||
{ id: 'url-length', priority: 'nice', item: 'URLs under 75 characters', automated: true },
|
||||
{ id: 'no-parameters', priority: 'nice', item: 'Clean URLs without parameters', automated: true }
|
||||
],
|
||||
|
||||
onPage: [
|
||||
{ id: 'title-tag', priority: 'critical', item: 'Unique title tag (50-60 chars)', automated: true },
|
||||
{ id: 'meta-description', priority: 'critical', item: 'Meta description (150-160 chars)', automated: true },
|
||||
{ id: 'h1-tag', priority: 'critical', item: 'Single H1 per page', automated: true },
|
||||
{ id: 'heading-hierarchy', priority: 'important', item: 'Proper heading hierarchy', automated: true },
|
||||
{ id: 'image-alt', priority: 'important', item: 'All images have alt text', automated: true },
|
||||
{ id: 'internal-links', priority: 'important', item: 'Internal linking structure', automated: false }
|
||||
],
|
||||
|
||||
performance: [
|
||||
{ id: 'lcp', priority: 'critical', item: 'LCP < 2.5s', automated: true },
|
||||
{ id: 'inp', priority: 'critical', item: 'INP < 200ms', automated: true },
|
||||
{ id: 'cls', priority: 'critical', item: 'CLS < 0.1', automated: true },
|
||||
{ id: 'mobile-friendly', priority: 'critical', item: 'Mobile-friendly design', automated: true },
|
||||
{ id: 'page-size', priority: 'important', item: 'Page size < 3MB', automated: true }
|
||||
],
|
||||
|
||||
structuredData: [
|
||||
{ id: 'jsonld', priority: 'important', item: 'JSON-LD structured data', automated: true },
|
||||
{ id: 'valid-schema', priority: 'important', item: 'Schema validates in Rich Results Test', automated: false },
|
||||
{ id: 'breadcrumbs', priority: 'nice', item: 'Breadcrumb schema', automated: true }
|
||||
]
|
||||
};
|
||||
|
||||
// Generate Audit Report
|
||||
export function generateSEOReport(results: Record<string, boolean>): string {
|
||||
let report = '# Technical SEO Audit Report\n\n';
|
||||
|
||||
Object.entries(technicalSEOChecklist).forEach(([category, items]) => {
|
||||
report += `## ${category.charAt(0).toUpperCase() + category.slice(1)}\n\n`;
|
||||
|
||||
items.forEach(item => {
|
||||
const status = results[item.id];
|
||||
const emoji = status ? '✅' : (item.priority === 'critical' ? '❌' : '⚠️');
|
||||
report += `${emoji} ${item.item}\n`;
|
||||
});
|
||||
|
||||
report += '\n';
|
||||
});
|
||||
|
||||
return report;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Checklist Summary
|
||||
|
||||
| Category | Critical | Important | Nice to Have |
|
||||
|----------|----------|-----------|--------------|
|
||||
| **Server** | HTTPS, Redirects | Response time | Security headers |
|
||||
| **Crawling** | robots.txt, Sitemap | Search Console | Crawl budget |
|
||||
| **Indexing** | Canonical, No duplicates | hreflang | Coverage report |
|
||||
| **URLs** | - | Readable, lowercase | Short URLs |
|
||||
| **On-Page** | Title, Meta, H1 | Alt text, Headings | Schema |
|
||||
| **Performance** | Core Web Vitals | Page size | Resource hints |
|
||||
|
||||
---
|
||||
|
||||
## Fazit
|
||||
|
||||
Technical SEO Checklist erfordert:
|
||||
|
||||
1. **Server-Basics**: HTTPS, Redirects, Security
|
||||
2. **Crawling**: robots.txt, Sitemap, Canonical
|
||||
3. **On-Page**: Title, Meta, Headings, Images
|
||||
4. **Performance**: Core Web Vitals optimieren
|
||||
|
||||
Regelmäßige Audits sichern die technische Basis.
|
||||
|
||||
---
|
||||
|
||||
## Bildprompts
|
||||
|
||||
1. "SEO audit dashboard showing checklist items, green checkmarks"
|
||||
2. "Technical SEO pyramid diagram, layered structure"
|
||||
3. "Search Console crawl stats visualization, data charts"
|
||||
|
||||
---
|
||||
|
||||
## Quellen
|
||||
|
||||
- [Google Search Central](https://developers.google.com/search/docs)
|
||||
- [Ahrefs Technical SEO Guide](https://ahrefs.com/blog/technical-seo/)
|
||||
- [Moz Technical SEO](https://moz.com/learn/seo/technical-seo)
|
||||
- [Web.dev SEO](https://web.dev/learn/seo/)
|
||||
Reference in New Issue
Block a user