Zum Inhalt springen
sw
en

Tippe um zu suchen

Server & Active Directory

IIS – Webserver Grundlagen

Internet Information Services (IIS) als Windows-Webserver einrichten, konfigurieren und betreiben – von der Installation bis zu SSL und Troubleshooting.

17 Min Lesezeit Fortgeschritten Zuletzt aktualisiert:

Was ist IIS und wann brauchst du ihn?

IIS (Internet Information Services) ist Microsofts eingebauter Webserver, der seit Windows NT mit dabei ist. Ab Windows Server 2008 R2 läuft er als IIS 7.0 oder höher mit einer vollständig modularen Architektur – du aktivierst nur, was du wirklich brauchst.

Als IT-Allrounder in einem KMU begegnest du IIS vor allem in diesen Szenarien:

  • Interne Webanwendungen hosten (z. B. ERP-Webfrontend, Intranet, Confluence-Alternativen)
  • Webdienste für Partner oder Kunden bereitstellen (REST-APIs, klassische ASP.NET-Apps)
  • Redirect-Server für interne URLs einrichten
  • Zertifikatsdienste (ADCS) oder WSUS über HTTPS veröffentlichen
  • Legacy-Applikationen mit klassischem ASP oder .NET Framework betreiben

IIS ist kein Apache oder nginx – er ist tief in Windows integriert, nutzt Windows-Authentifizierung direkt und lässt sich per PowerShell und GPO steuern. Wenn du eine Windows-lastiges Umfeld hast, ist IIS oft die einfachste Wahl.

IIS installieren

IIS ist eine Windows Server-Rolle, die du über den Server-Manager oder PowerShell aktivierst. Sie ist nicht standardmässig aktiv.

Minimalinstallation (nur Webserver):

Install-WindowsFeature -Name Web-Server -IncludeManagementTools

Erweiterte Installation für .NET-Anwendungen:

# Webserver inkl. .NET 4.5 und ASP.NET
Install-WindowsFeature -Name Web-Server, Web-Asp-Net45, Web-Net-Ext45 -IncludeManagementTools

# Alle gängigen Features auf einmal (typischer App-Server)
Install-WindowsFeature -Name Web-Server `
    -IncludeAllSubFeature `
    -IncludeManagementTools

Installation prüfen:

Get-WindowsFeature -Name Web-*  | Where-Object { $_.Installed -eq $true }

Nach der Installation erreichst du den IIS Manager so:

inetmgr

Oder: Win+Rinetmgr → Enter.

Kernkonzepte im Überblick

Bevor du loslegst, musst du diese fünf Begriffe verstehen. Alles andere baut darauf auf:

BegriffWas es ist
SiteEine Website mit mindestens einem Binding (IP + Port + Hostname) und einem physischen Pfad
Application PoolIsolierter W3WP-Prozess, der eine oder mehrere Apps ausführt. Hängt ein Pool, hängt die App – andere Pools laufen weiter
BindingKombination aus Protokoll (http/https), IP-Adresse, Port und optionalem Host-Header
Virtual DirectoryEin Unterordner in einer Site, der auf einen anderen physischen Pfad zeigt
Handler MappingLegt fest, welche Dateitypen IIS selbst verarbeitet (.aspx, .php, .html) und welche er an externe Prozesse weiterleitet

Wie ein Request läuft:

Browser → TCP-Port 80/443 → http.sys (Kernel-Mode) → W3SVC → Application Pool (W3WP.exe) → Handler → Response

Wichtig: http.sys ist der Kernel-Treiber, der den Port hält. Deshalb können mehrere Websites auf Port 80 laufen, solange sie unterschiedliche Host-Header haben – http.sys routet vorher nach Host-Header.

Erste Website erstellen

Schritt 1 – Verzeichnis und Inhalt vorbereiten

# Webroot anlegen
New-Item -Path "C:\inetpub\intranet.firma.ch" -ItemType Directory

# Test-Startseite erstellen
Set-Content -Path "C:\inetpub\intranet.firma.ch\index.html" -Value "<h1>Intranet Firma</h1>"

# NTFS-Berechtigungen: IIS_IUSRS muss lesen dürfen
icacls "C:\inetpub\intranet.firma.ch" /grant "IIS_IUSRS:(OI)(CI)R" /T

Schritt 2 – Application Pool erstellen

# Neuen Pool mit .NET CLR Version 4.0
New-WebAppPool -Name "Intranet-Pool"

# .NET-Version setzen (v4.0 = .NET Framework 4.x, "No Managed Code" für reine Static Sites oder .NET Core)
Set-ItemProperty "IIS:\AppPools\Intranet-Pool" -Name managedRuntimeVersion -Value "v4.0"

# Identität setzen (ApplicationPoolIdentity = sicherste Option; alternativ ein Dienstkonto)
Set-ItemProperty "IIS:\AppPools\Intranet-Pool" -Name processModel.identityType -Value 4

Schritt 3 – Site erstellen

# Site erstellen (HTTP auf Port 80 mit Host-Header)
New-Website -Name "Intranet" `
    -Port 80 `
    -HostHeader "intranet.firma.ch" `
    -PhysicalPath "C:\inetpub\intranet.firma.ch" `
    -ApplicationPool "Intranet-Pool"

# Site starten
Start-Website -Name "Intranet"

# Prüfen
Get-Website -Name "Intranet"

Schritt 4 – HTTPS-Binding ergänzen

# Bestehendes Zertifikat finden (z.B. nach Subject-Name)
$cert = Get-ChildItem -Path Cert:\LocalMachine\My |
    Where-Object { $_.Subject -like "*intranet.firma.ch*" } |
    Select-Object -First 1

Write-Host "Thumbprint: $($cert.Thumbprint)"

# HTTPS-Binding hinzufügen
New-WebBinding -Name "Intranet" -Protocol "https" -Port 443 -HostHeader "intranet.firma.ch" -SslFlags 1

# Zertifikat ans Binding knüpfen (SNI = SslFlags 1)
$binding = Get-WebBinding -Name "Intranet" -Protocol "https"
$binding.AddSslCertificate($cert.Thumbprint, "My")

Application Pools richtig konfigurieren

Application Pools sind das wichtigste Isolations-Werkzeug in IIS. Jeder Pool läuft als eigener W3WP.exe-Prozess.

Wichtige Pool-Einstellungen:

# Prozess-Identität auf ein AD-Dienstkonto setzen
Set-ItemProperty "IIS:\AppPools\Intranet-Pool" `
    -Name processModel `
    -Value @{userName="FIRMA\svc-intranet"; password="Passwort123!"; identityType="SpecificUser"}

# Automatischer Neustart bei Speicherproblemen (300 MB)
Set-ItemProperty "IIS:\AppPools\Intranet-Pool" `
    -Name recycling.periodicRestart.privateMemory -Value 307200

# Idle-Timeout erhöhen (Standard: 20 Min – App wird nach Inaktivität beendet)
Set-ItemProperty "IIS:\AppPools\Intranet-Pool" `
    -Name processModel.idleTimeout -Value "00:30:00"

# 32-Bit aktivieren (für alte 32-Bit-DLLs)
Set-ItemProperty "IIS:\AppPools\Intranet-Pool" `
    -Name enable32BitAppOnWin64 -Value $true

Pool manuell recyclen (wenn die App hängt):

Restart-WebAppPool -Name "Intranet-Pool"

Warum getrennte Pools?

Wenn App-A einen Memory Leak hat und den Pool zum Absturz bringt, läuft App-B in ihrem eigenen Pool weiter. Faustregel: Eine App = ein Pool.

web.config – Konfiguration auf Dateiebene

Die web.config im Webroot-Verzeichnis steuert das Verhalten einer einzelnen Site oder App. Sie wird von IIS automatisch geladen und kann Einstellungen aus der ApplicationHost.config überschreiben.

Typische web.config für statische Site mit Redirect:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <system.webServer>

    <!-- HTTP auf HTTPS umleiten -->
    <rewrite>
      <rules>
        <rule name="HTTP zu HTTPS" stopProcessing="true">
          <match url="(.*)" />
          <conditions>
            <add input="{HTTPS}" pattern="^OFF$" />
          </conditions>
          <action type="Redirect" url="https://{HTTP_HOST}/{R:1}" redirectType="Permanent" />
        </rule>
      </rules>
    </rewrite>

    <!-- Standarddokument -->
    <defaultDocument>
      <files>
        <add value="index.html" />
        <add value="default.aspx" />
      </files>
    </defaultDocument>

    <!-- Verzeichnislisting deaktivieren -->
    <directoryBrowse enabled="false" />

    <!-- Sicherheits-Header -->
    <httpProtocol>
      <customHeaders>
        <add name="X-Content-Type-Options" value="nosniff" />
        <add name="X-Frame-Options" value="SAMEORIGIN" />
        <add name="Strict-Transport-Security" value="max-age=31536000; includeSubDomains" />
      </customHeaders>
    </httpProtocol>

  </system.webServer>
</configuration>

IIS per PowerShell verwalten

Der IIS-Manager (inetmgr) ist gut für einzelne Anpassungen. Für Scripting und Automatisierung nutzt du das WebAdministration-Modul:

# Modul laden
Import-Module WebAdministration

# Alle Sites anzeigen
Get-Website

# Alle Application Pools anzeigen
Get-WebAppPool

# Site stoppen / starten
Stop-Website -Name "Intranet"
Start-Website -Name "Intranet"

# Bindings einer Site anzeigen
Get-WebBinding -Name "Intranet"

# Binding entfernen
Remove-WebBinding -Name "Intranet" -Protocol "http" -Port 80

# Physischen Pfad einer Site ändern
Set-ItemProperty "IIS:\Sites\Intranet" -Name physicalPath -Value "C:\inetpub\intranet-v2"

# Site-Zustand (Running / Stopped)
(Get-Website -Name "Intranet").State

# Alle gestoppten Sites finden
Get-Website | Where-Object { $_.State -eq "Stopped" }

Application Request Routing (ARR) – Reverse Proxy und Load Balancer

Sobald du mehr als eine Applikation oder mehr als einen Webserver betreibst, reicht die klassische IIS-Konfiguration von oben nicht mehr aus. Application Request Routing (ARR) ist eine kostenlose Erweiterung von Microsoft, die IIS zu einem waschechten Reverse Proxy und Layer-7-Load-Balancer macht.

Typische Einsatzszenarien:

  • Mehrere Backend-Server hinter einer einzigen öffentlichen URL verstecken (z. B. intranet.firma.ch verteilt auf drei App-Server)
  • SSL-Offloading: TLS wird am ARR-Server terminiert, die Backends sprechen nur noch HTTP intern
  • Pfad-basiertes Routing (z. B. /api auf Backend A, /app auf Backend B)
  • Reverse Proxy vor Nicht-IIS-Diensten (Node.js, Tomcat, Docker-Container)

Installation: ARR ist nicht Teil der Windows-Server-Rolle und muss separat installiert werden. Es setzt zusätzlich das URL Rewrite Module voraus (dasselbe, das schon für die web.config-Weiterleitungen weiter oben nötig war).

# Beide Erweiterungen als MSI installieren:
# URL Rewrite: https://www.iis.net/downloads/microsoft/url-rewrite
# ARR 3.0:     https://www.iis.net/downloads/microsoft/application-request-routing

# Nach der Installation: Proxy-Funktion global aktivieren
Set-WebConfigurationProperty -Filter "system.webServer/proxy" -PSPath "IIS:\" -Name "enabled" -Value $true

Server-Farm anlegen (im IIS-Manager unter “Server Farms”, oder per appcmd):

%windir%\system32\inetsrv\appcmd.exe add serverfarm /name:"Intranet-Farm"
%windir%\system32\inetsrv\appcmd.exe add server "Intranet-Farm/[address='10.0.1.11']"
%windir%\system32\inetsrv\appcmd.exe add server "Intranet-Farm/[address='10.0.1.12']"

Danach braucht es eine Weiterleitungsregel, die eingehende Requests an die Farm schickt:

<rewrite>
  <rules>
    <rule name="Reverse Proxy zu Intranet-Farm" stopProcessing="true">
      <match url="(.*)" />
      <action type="Rewrite" url="http://Intranet-Farm/{R:1}" />
    </rule>
  </rules>
</rewrite>

Load-Balancing-Algorithmen wählst du pro Farm aus:

AlgorithmusWann sinnvoll
Weighted Round RobinStandard – gleichmässige Verteilung, optional gewichtet für unterschiedlich starke Server
Least Current RequestServer mit den wenigsten offenen Requests bekommt den nächsten Request – gut bei unterschiedlicher Antwortdauer
Request Hash / Client AffinityBindet einen Client per Cookie oder Hash an denselben Server – nötig bei serverseitigem Session-State ohne zentralen Session-Store

SSL-Offloading: Terminierst du TLS am ARR-Server, ergänze die Rewrite-Regel um Server-Variablen, damit Backends die ursprüngliche Client-IP und das Protokoll kennen (die Variablen müssen zuvor unter allowedServerVariables freigeschaltet werden):

<rewrite>
  <allowedServerVariables>
    <add name="HTTP_X_FORWARDED_PROTO" />
    <add name="HTTP_X_FORWARDED_FOR" />
  </allowedServerVariables>
  <rules>
    <rule name="Reverse Proxy zu Intranet-Farm" stopProcessing="true">
      <match url="(.*)" />
      <serverVariables>
        <set name="HTTP_X_FORWARDED_PROTO" value="https" />
        <set name="HTTP_X_FORWARDED_FOR" value="{REMOTE_ADDR}" />
      </serverVariables>
      <action type="Rewrite" url="http://Intranet-Farm/{R:1}" />
    </rule>
  </rules>
</rewrite>

Shared Configuration und Central Certificate Store für IIS-Farmen

Sobald mehrere IIS-Server dieselbe Konfiguration fahren sollen (z. B. alle Knoten hinter der ARR-Farm von oben), willst du nicht auf jedem Server einzeln Sites, Pools und Bindings pflegen. Zwei Microsoft-Bordmittel lösen genau das.

Shared Configuration exportiert applicationHost.config, administration.config und die Verschlüsselungs-Keys auf eine zentrale Freigabe. Alle Farm-Mitglieder lesen von dort – eine Änderung auf einem Server wirkt sofort auf alle anderen.

# Auf dem ersten Server: Konfiguration exportieren
# IIS-Manager → Server-Knoten → "Shared Configuration" → "Export Configuration" → UNC-Pfad wählen

# Auf jedem weiteren Server: "Enable shared configuration" mit UNC-Pfad,
# z.B. \\fileserver\iis-config, plus Konto mit Lesezugriff auf die Freigabe

# Kontrolle: aktuell geladene Konfiguration prüfen
Get-IISConfigSection -SectionPath "system.applicationHost/configHistory"

Central Certificate Store (CCS) löst dasselbe Problem für SSL-Zertifikate: Statt das Zertifikat in den lokalen Zertifikat-Store jedes Servers zu importieren, liegt es einmal auf einer Netzwerkfreigabe, und jeder IIS-Server liest es beim TLS-Handshake von dort. Die Namenskonvention der PFX-Dateien ist zwingend einzuhalten:

ZertifikatstypErwarteter Dateiname
Einzelner Hostnameintranet.firma.ch.pfx
Wildcard-Zertifikat_.firma.ch.pfx
Mehrere Hostnamen/SANsein File pro Name, z. B. shop.firma.ch.pfx und shop2.firma.ch.pfx
# CCS-Feature installieren
Install-WindowsFeature -Name Web-CertProvider

# Freigabe für die Zertifikate anlegen (auf einem dedizierten Fileserver, nicht dem Content-Server)
New-Item -Path "\\fileserver\ccs" -ItemType Directory

# Auf jedem IIS-Knoten: CCS aktivieren
# IIS-Manager → Server-Knoten → "Centralized Certificates" → aktivieren,
# UNC-Pfad, Benutzername/Passwort und ggf. Passwort für den privaten Schlüssel eintragen

Beim Binding wählst du danach statt eines lokalen Zertifikats “Central Certificate Store” – IIS matcht automatisch über den Host-Header aus dem SNI-Handshake.

Automatisierte Zertifikatserneuerung mit win-acme

Let’s-Encrypt-Zertifikate sind kostenlos, laufen aber nur 90 Tage – ohne Automatisierung ist das ein Garant für den nächsten “Zertifikat abgelaufen”-Vorfall. win-acme (wacs.exe) ist der De-facto-Standard-ACME-Client für Windows/IIS: portabel, kein zusätzlicher Serverdienst nötig, direkte IIS-Integration.

# win-acme herunterladen und entpacken (z.B. nach C:\Tools\win-acme)
# https://www.win-acme.com

# Interaktiver Erstlauf – erkennt IIS-Sites automatisch
.\wacs.exe

# Unattended-Variante für eine bestimmte Site, z.B. aus einem Deployment-Skript:
.\wacs.exe --source iis --siteid 1 `
    --host intranet.firma.ch `
    --validation selfhosting `
    --store certificatestore `
    --installation iis `
    --accepttos `
    --emailaddress it@firma.ch

Nach dem ersten erfolgreichen Lauf legt win-acme selbst einen geplanten Task an, der täglich prüft, ob eine Erneuerung fällig ist (Standardwert: 55 Tage nach Ausstellung, konfigurierbar in settings.json). Kein Cron-Job, kein manuelles Nachpflegen nötig.

# Manuelle Erneuerung erzwingen (z.B. zum Testen)
.\wacs.exe --renew --force --verbose

# Alle verwalteten Zertifikate auflisten
.\wacs.exe --list

Für IIS-Farmen mit Central Certificate Store kannst du win-acme direkt in den CCS-Ordner ausstellen lassen, statt in den lokalen Zertifikat-Store eines einzelnen Servers:

.\wacs.exe --source iis --siteid 1 `
    --host intranet.firma.ch `
    --store centralssl `
    --centralsslstore "\\fileserver\ccs" `
    --installation none `
    --accepttos --emailaddress it@firma.ch

Damit landet die PFX-Datei automatisch mit dem korrekten Namen (intranet.firma.ch.pfx) auf der Freigabe, und alle Farm-Mitglieder ziehen sie beim nächsten TLS-Handshake.

Mehr zu Zertifikaten allgemein (Ketten, Trust-Store, Ablauf-Monitoring) unter SSL/TLS-Zertifikate. Für interne Hostnamen, die öffentlich nicht validierbar sind, ist statt Let’s Encrypt oft eine eigene ADCS/PKI die passende Quelle.

Health Checks und Application Initialization für Zero-Downtime

Zwei Bausteine sorgen dafür, dass Wartungsarbeiten und Deployments nicht sofort einen Nutzer-Ausfall bedeuten.

ARR Health Checks (URL Test): ARR kann Backend-Server einer Farm periodisch per HTTP-Request testen und automatisch aus der Rotation nehmen, wenn sie nicht antworten oder falsche Inhalte liefern.

%windir%\system32\inetsrv\appcmd.exe set config -section:webFarms ^
    /"[name='Intranet-Farm'].applicationRequestRouting.healthCheck.url:http://localhost/health" ^
    /"[name='Intranet-Farm'].applicationRequestRouting.healthCheck.interval:00:00:30" ^
    /"[name='Intranet-Farm'].applicationRequestRouting.healthCheck.responseMatch:OK"

Praktisch: Die Route /health sollte mehr prüfen als “IIS läuft” – z. B. ob die Datenbankverbindung steht. So erkennt ARR auch halb-defekte Backends, nicht nur komplett abgestürzte.

Application Initialization löst ein anderes Problem: Ein neu gestarteter oder recycelter Application Pool muss bei .NET-Apps oft erst JIT-kompilieren, Caches aufwärmen oder Verbindungen aufbauen. Ohne Vorwärmung bekommt der erste echte Nutzer-Request diese Verzögerung ab – oder schlimmer, einen Timeout.

# Feature installieren
Install-WindowsFeature -Name Web-AppInit

# Pool so konfigurieren, dass er beim Start sofort "warmläuft" statt auf den ersten Request zu warten
Set-ItemProperty "IIS:\AppPools\Intranet-Pool" -Name startMode -Value "AlwaysRunning"
<!-- in der web.config der Applikation -->
<system.webServer>
  <applicationInitialization doAppInitAfterRestart="true">
    <add initializationPage="/health" />
  </applicationInitialization>
</system.webServer>

Kombiniert ergibt sich ein einfaches Blue/Green-Deployment: Du deployst die neue Version auf den gestoppten “grünen” Server, lässt Application Initialization ihn warmlaufen, prüfst per Health-Check-URL manuell, dass alles passt, und schaltest dann die ARR-Farm auf den grünen Server um – während der alte “blaue” Server als Rollback-Option bereitsteht.

Performance: Output-Caching, Kompression und Connection-Limits

Bevor du über zusätzliche Hardware oder mehr Farm-Knoten nachdenkst, hol die Reserven aus der bestehenden IIS-Konfiguration.

Output-Caching speichert komplette Antworten im Kernel-Mode-Cache von http.sys – die schnellstmögliche Auslieferung, weil der Request den Application Pool gar nicht erst erreicht:

<system.webServer>
  <caching enabled="true" enableKernelCache="true">
    <profiles>
      <add extension=".jpg" policy="CacheForTimePeriod" kernelCachePolicy="CacheForTimePeriod" duration="1.00:00:00" />
      <add extension=".css" policy="CacheForTimePeriod" kernelCachePolicy="CacheForTimePeriod" duration="01:00:00" />
    </profiles>
  </caching>
</system.webServer>

Kompression reduziert die übertragene Datenmenge spürbar – bei textbasierten Antworten (HTML, JSON, CSS, JS) oft um 60 bis 80 Prozent:

<system.webServer>
  <urlCompression doStaticCompression="true" doDynamicCompression="true" />
  <httpCompression>
    <dynamicTypes>
      <add mimeType="application/json" enabled="true" />
      <add mimeType="text/*" enabled="true" />
    </dynamicTypes>
  </httpCompression>
</system.webServer>

Connection-Limits schützen davor, dass eine einzelne Applikation oder ein einzelner Client den ganzen Server lahmlegt:

# Maximal gleichzeitige Verbindungen pro Site begrenzen
Set-WebConfigurationProperty -Filter "system.applicationHost/sites/site[@name='Intranet']/limits" `
    -PSPath "IIS:\" -Name "maxConnections" -Value 5000

# Queue-Länge des Application Pools (Requests, die auf einen freien Worker-Thread warten)
Set-ItemProperty "IIS:\AppPools\Intranet-Pool" -Name queueLength -Value 2000

Zusammen mit den Application-Pool-Recycling-Einstellungen von weiter oben (Idle-Timeout, Memory-Limit) bilden diese Werte dein Sicherheitsnetz gegen Überlast – wichtig, bevor ein einzelner fehlerhafter Client per Retry-Loop den ganzen Pool flutet.

Erweiterte Authentifizierung: Client-Zertifikate und ADFS/OIDC-Reverse-Proxy

Client-Zertifikat-Authentifizierung eignet sich, wenn Passwörter oder NTLM nicht ausreichen – typisch für Partner-APIs, Maschine-zu-Maschine-Kommunikation oder hochsensible interne Anwendungen.

# Feature installieren
Install-WindowsFeature -Name Web-Client-Auth, Web-Cert-Auth

# SSL-Settings: Client-Zertifikate zwingend verlangen
Set-WebConfiguration -Filter "system.webServer/security/access" `
    -PSPath "IIS:\Sites\Intranet" -Value @{sslFlags="Ssl,SslRequireCert"}

# Client Certificate Mapping aktivieren (Zertifikat wird einem AD-Konto zugeordnet)
Set-WebConfigurationProperty -Filter "system.webServer/security/authentication/iisClientCertificateMappingAuthentication" `
    -PSPath "IIS:\Sites\Intranet" -Name "enabled" -Value $true

Wichtige Statuscodes beim Troubleshooting: 403.7 bedeutet, der Client hat gar kein Zertifikat mitgeschickt; 403.16 bedeutet, das Zertifikat wurde zwar gesendet, ist aber nicht vertrauenswürdig, weil die Root-CA im Trust-Store des Servers fehlt. Für intern ausgestellte Zertifikate ist dafür meist eine eigene ADCS/PKI die richtige Quelle.

IIS als Reverse Proxy vor ADFS oder einem OIDC-Provider: In gewachsenen Umgebungen steht IIS mit ARR manchmal vor einem Identity Provider, um dessen interne Adresse nicht direkt im Internet zu exponieren oder um zusätzliche Header- und WAF-Regeln davorzuschalten.

<rewrite>
  <rules>
    <rule name="Proxy zu ADFS" stopProcessing="true">
      <match url="^adfs/(.*)" />
      <serverVariables>
        <set name="HTTP_X_FORWARDED_HOST" value="{HTTP_HOST}" />
        <set name="HTTP_X_FORWARDED_PROTO" value="https" />
      </serverVariables>
      <action type="Rewrite" url="https://adfs-internal.firma.ch/adfs/{R:1}" />
    </rule>
  </rules>
</rewrite>

Beim reinen OIDC-Reverse-Proxy (z. B. vor einer Applikation, die selbst gegen Entra ID oder Keycloak authentifiziert) reicht IIS/ARR meist als transparenter Layer-7-Proxy ohne Sonderlogik – die eigentliche Token-Validierung passiert in der Applikation dahinter, nicht in IIS selbst.

Logs analysieren

IIS-Logs liegen standardmässig hier:

C:\inetpub\logs\LogFiles\W3SVC1\   # Site ID 1
C:\inetpub\logs\LogFiles\W3SVC2\   # Site ID 2

Das Format ist W3C Extended Log Format – ein Leerzeichen-getrenntes Textformat:

#Fields: date time s-ip cs-method cs-uri-stem cs-uri-query s-port cs-username c-ip cs(User-Agent) sc-status sc-substatus sc-win32-status time-taken
2026-06-23 08:15:42 192.168.10.5 GET /api/status - 443 - 192.168.10.20 Mozilla/5.0... 200 0 0 45

Wichtige HTTP-Statuscodes in IIS-Logs:

StatusBedeutung
200OK
301 / 302Redirect (permanent / temporär)
401Nicht autorisiert (Authentifizierung fehlt)
403Verboten (Zugriff verweigert, z. B. fehlende NTFS-Rechte)
404Nicht gefunden
500Interner Serverfehler (App-Fehler)
503Service Unavailable (Application Pool gestoppt oder überlastet)

Logs per PowerShell auswerten:

# Alle 500er der letzten Stunde
$logPath = "C:\inetpub\logs\LogFiles\W3SVC1\"
Get-Content "$logPath\u_ex$(Get-Date -Format 'yyMMdd').log" |
    Where-Object { $_ -match " 500 " } |
    Select-Object -Last 20

# Anzahl Requests pro Stunde zählen (quick & dirty)
Get-Content "$logPath\u_ex$(Get-Date -Format 'yyMMdd').log" |
    Where-Object { $_ -notmatch "^#" } |
    ForEach-Object { $_.Split(" ")[1].Substring(0,2) } |
    Group-Object | Sort-Object Name

Tipp: Für ernsthafte Log-Analyse lohnt sich Log Parser Studio von Microsoft – kostenlos, SQL-ähnliche Abfragesprache auf IIS-Logs.

Troubleshooting – typische Probleme

503 Service Unavailable

Der Application Pool ist gestoppt oder wurde durch einen Fehler beendet.

# Pool-Status prüfen
Get-WebAppPool | Select-Object Name, State

# Pool manuell starten
Start-WebAppPool -Name "Intranet-Pool"

# Windows Ereignisanzeige auf Pool-Crashes prüfen
Get-EventLog -LogName System -Source "WAS" -Newest 20

Typische Ursachen: Memory Limit erreicht, Absturz des Worker-Prozesses, falsche Pool-Identität.

401 Unauthorized – Endlosschleife

Passiert, wenn Windows-Authentifizierung aktiviert ist, aber der Browser kein NTLM/Kerberos-Ticket schickt (z.B. bei falscher SPN-Konfiguration oder externem Zugriff).

# Authentifizierungsmodule einer Site prüfen
Get-WebConfiguration -Filter "system.webServer/security/authentication/*" -Location "Intranet" |
    Select-Object PSPath, enabled

500.19 – web.config nicht lesbar

Fehler in der web.config-Syntax oder fehlende Berechtigungen auf die Datei.

# Direkter Hinweis auf Zeile und Fehler im IIS-Manager
# Oder: Syntax im Browser prüfen – IIS zeigt die fehlerhafte Zeile an

403.14 – Directory Listing nicht erlaubt

Kein Standarddokument (index.html, default.aspx) gefunden und Verzeichnislisting deaktiviert.

# Standarddokument ergänzen
Add-WebConfiguration -Filter "system.webServer/defaultDocument/files" `
    -Location "Intranet" -Value @{value="app.html"}

Fehler 0x80070005 – Access Denied

Klassischer Berechtigungsfehler. Der Application Pool hat keinen Lesezugriff auf den Webroot.

# Wer ist die Pool-Identität?
(Get-ItemProperty "IIS:\AppPools\Intranet-Pool" processModel).userName
# Leer = ApplicationPoolIdentity → NTFS-Gruppe ist "IIS AppPool\Intranet-Pool"

# Berechtigung gezielt setzen
icacls "C:\inetpub\intranet.firma.ch" /grant "IIS AppPool\Intranet-Pool:(OI)(CI)R" /T

Sicherheits-Checkliste für IIS

Diese Punkte solltest du bei jeder produktiven IIS-Installation abhaken:

# 1. IIS-Version aus HTTP-Header entfernen (kein Fingerprinting)
Set-WebConfigurationProperty -Filter "system.webServer/security/requestFiltering" `
    -PSPath "IIS:\" -Name "removeServerHeader" -Value $true

# 2. HTTP-Methoden einschränken (nur GET und POST erlauben)
# in web.config:
# <requestFiltering>
#   <verbs allowUnlisted="false">
#     <add verb="GET" allowed="true" />
#     <add verb="POST" allowed="true" />
#   </verbs>
# </requestFiltering>

# 3. Verzeichnislisting global deaktivieren
Set-WebConfigurationProperty -Filter "system.webServer/directoryBrowse" `
    -PSPath "IIS:\" -Name "enabled" -Value $false

# 4. Request-Grösse begrenzen (Standard: 30 MB, für APIs kleiner setzen)
Set-WebConfigurationProperty -Filter "system.webServer/security/requestFiltering" `
    -PSPath "IIS:\" -Name "maxAllowedContentLength" -Value 10485760  # 10 MB

# 5. Fehlerseiten ohne Details nach aussen zeigen
Set-WebConfigurationProperty -Filter "system.webServer/httpErrors" `
    -PSPath "IIS:\" -Name "errorMode" -Value "DetailedLocalOnly"

Thematisch passende Seiten in diesem Wiki:

  • SSL/TLS-Zertifikate – Zertifikate beantragen, installieren und ans IIS-Binding knüpfen
  • ADCS/PKI-Grundlagen – eigene interne CA betreiben, wenn öffentliche Zertifikate (Let’s Encrypt) nicht infrage kommen
  • Entra ID-Grundlagen – moderne OIDC/SAML-Authentifizierung als Alternative zu einer klassischen ADFS-Farm
  • Windows-Firewall – Port 80 und 443 in der Windows-Firewall freigeben
  • DNS-Grundlagen – Ohne korrekten DNS-Eintrag (A-Record oder CNAME) ist die Site nicht erreichbar
  • PowerShell IT-Alltag – Grundlagen für die PowerShell-Befehle auf dieser Seite
  • Monitoring-Grundlagen – Health-Check-Endpunkte und Alerting für IIS-Farmen sinnvoll überwachen

Weiterlernen

Videos

YouTube
How to Install a Windows 2022 IIS Webserver (Step by Step)
YouTube
How to Install and Configure IIS Web Server in Windows Server 2022

Kommentare

Frage, Verbesserungsvorschlag oder eigene Erfahrung zu diesem Artikel? Schreib einen Kommentar. Neue Beiträge erscheinen nach kurzer Moderation.

  • Lade Kommentare …
Kommentar schreiben