Menu
Nazad na Blog
2 min read
SEO

Technical SEO Checklist

Komplette Technical SEO Checklist für 2026. Crawling, Indexierung, Site Structure und Performance-Optimierung für bessere Rankings.

Technical SEOSEO ChecklistCrawlingIndexierungSite StructurePage Speed
Technical SEO Checklist

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

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

// 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];
}
// 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

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

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

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

CategoryCriticalImportantNice to Have
**Server**HTTPS, RedirectsResponse timeSecurity headers
**Crawling**robots.txt, SitemapSearch ConsoleCrawl budget
**Indexing**Canonical, No duplicateshreflangCoverage report
**URLs**-Readable, lowercaseShort URLs
**On-Page**Title, Meta, H1Alt text, HeadingsSchema
**Performance**Core Web VitalsPage sizeResource 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