NTLM vs Kerberos Authentication
:When Nodinite Web Client and Web API are accessed using Windows Authentication, the browser must use Kerberos for good performance. If the browser silently falls back to NTLM, every HTTP request triggers hundreds of LSA SID lookup calls against the Domain Controller, causing 4+ second response times and DC flooding.
Important
Typical symptom:
curlis fast, browsers and Swagger UI are extremely slow (4-30 seconds per page). Adding the site to the Local Intranet Zone immediately fixes the problem. This confirms NTLM fallback as the root cause.
Root Cause
Why NTLM Falls Back from Kerberos
| Client | Default Authentication | Behaviour |
|---|---|---|
| curl | Negotiate → Kerberos | ✅ Fast — single Kerberos ticket exchange |
| Microsoft Edge / Chrome | Kerberos only for Intranet Zone sites | ❌ Falls back to NTLM for Internet Zone sites |
PowerShell Invoke-WebRequest |
Negotiate / NTLM | ❌ May use NTLM if Kerberos ticket unavailable |
| ASP.NET Core (Server) | Supports both via SPNEGO | Correctly handles both — not the problem |
Modern browsers only send Kerberos tickets to sites in the Local Intranet Zone. If the Nodinite URL is not in that zone, the browser falls back to NTLM, causing the LSA flood.
Why NTLM Causes LSA Floods
Each NTLM-authenticated HTTP request requires the server to resolve the user's full Windows security token:
- Browser sends NTLM challenge/response (3+ round-trips per request)
- IIS worker (
w3wp.exe) receives the authenticated identity - Nodinite authorisation code resolves group names:
WindowsIdentity.Groups.Select(s => s.Translate(typeof(NTAccount))) - Each
Translate()call triggers aLookupSidsrequest to the Domain Controller - A user in a large Active Directory may have 50-200+ group SIDs in their token
- This means 50-200+ DC round-trips per page load
Evidence in Windows Security Logs
When the LSA flood is occurring, the Windows Security log and LSA diagnostic log show entries like:
[ 2/20 22:14:00] LspDsLookup - LookupSids request for 1 SIDs ...
Sids[ 0 ] = S-1-5-21-2138058708-701738657-3842216330-3208
Requestor details: Local Machine, Process ID = 13328,
Process Name = C:\Windows\System32\inetsrv\w3wp.exe
The w3wp.exe process resolving individual SIDs one by one, repeatedly, within the same second.
Why Kerberos Does Not Have This Problem
With Kerberos:
- Authentication uses a service ticket from the KDC — single round-trip, cached by the client
- Subsequent requests reuse the cached ticket with no re-authentication overhead
- Group SID resolution still occurs, but Kerberos connections are persistent and overall auth overhead is minimal
Diagnostic Script
Use the following PowerShell 7 script to identify whether the performance issue is caused by NTLM fallback. Save as kerberoscheck.ps1 and run from the client workstation experiencing slow performance (not the server).
#Requires -Version 7.0
<#
.SYNOPSIS
Kerberos vs NTLM Authentication Diagnostic Script for Nodinite Web API / Web Client.
.DESCRIPTION
This script verifies whether a target Nodinite endpoint is using Kerberos or NTLM
authentication. NTLM fallback causes severe performance degradation due to LSA SID
lookup floods against the Domain Controller on every HTTP request.
.PARAMETER Url
The URL of the Nodinite Web API or Web Client endpoint to test.
Example: https://nodinite.yourdomain.com/api/isalive
.PARAMETER CheckSPN
If set, checks SPN registration for the target host.
.EXAMPLE
.\kerberoscheck.ps1 -Url "https://nodinite.yourdomain.com/api/isalive"
.EXAMPLE
.\kerberoscheck.ps1 -Url "https://nodinite.yourdomain.com/api/isalive" -CheckSPN
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$Url,
[switch]$CheckSPN
)
$ErrorActionPreference = 'Stop'
Write-Host "============================================================" -ForegroundColor Cyan
Write-Host " Nodinite Kerberos / NTLM Authentication Diagnostic Tool" -ForegroundColor Cyan
Write-Host "============================================================" -ForegroundColor Cyan
Write-Host ""
# 1. Parse the target host from the URL
$uri = [System.Uri]::new($Url)
$targetHost = $uri.Host
$targetPort = $uri.Port
$scheme = $uri.Scheme
Write-Host "[INFO] Target Host : $targetHost" -ForegroundColor Yellow
Write-Host "[INFO] Target Port : $targetPort" -ForegroundColor Yellow
Write-Host "[INFO] Scheme : $scheme" -ForegroundColor Yellow
Write-Host ""
# 2. Check current user and domain info
Write-Host "--- Current User & Domain Info ---" -ForegroundColor Green
Write-Host " Username : $($env:USERNAME)"
Write-Host " Domain : $($env:USERDOMAIN)"
Write-Host " Computer : $($env:COMPUTERNAME)"
Write-Host " DNS Domain : $($env:USERDNSDOMAIN)"
Write-Host ""
# 3. Check DNS resolution
Write-Host "--- DNS Resolution ---" -ForegroundColor Green
try {
$dns = Resolve-DnsName -Name $targetHost -ErrorAction Stop
foreach ($record in $dns) {
Write-Host " $($record.Name) -> $($record.IPAddress) ($($record.QueryType))"
}
}
catch {
Write-Host " [ERROR] DNS resolution failed for $targetHost : $_" -ForegroundColor Red
}
Write-Host ""
# 4. Check Kerberos ticket cache (klist)
Write-Host "--- Kerberos Ticket Cache (klist) ---" -ForegroundColor Green
try {
$klistOutput = & klist 2>&1
$httpTickets = $klistOutput | Select-String -Pattern "HTTP/" -Context 2, 2
if ($httpTickets) {
Write-Host " [OK] Found HTTP service tickets:" -ForegroundColor Green
foreach ($ticket in $httpTickets) {
Write-Host " $($ticket.Line.Trim())"
}
}
else {
Write-Host " [WARN] No HTTP service tickets found in the cache." -ForegroundColor Yellow
Write-Host " This may indicate Kerberos is not being used for HTTP services." -ForegroundColor Yellow
Write-Host ""
Write-Host " Full klist output:" -ForegroundColor Yellow
$klistOutput | ForEach-Object { Write-Host " $_" }
}
}
catch {
Write-Host " [ERROR] Could not run klist: $_" -ForegroundColor Red
}
Write-Host ""
# 5. Check SPN registration (optional)
if ($CheckSPN) {
Write-Host "--- SPN Registration Check ---" -ForegroundColor Green
try {
$spnOutput = & setspn -Q "HTTP/$targetHost" 2>&1
Write-Host " setspn -Q HTTP/$targetHost :" -ForegroundColor Yellow
$spnOutput | ForEach-Object { Write-Host " $_" }
Write-Host ""
if ($targetHost -notmatch '\.') {
$fqdn = "$targetHost.$($env:USERDNSDOMAIN)"
Write-Host " setspn -Q HTTP/$fqdn :" -ForegroundColor Yellow
$spnFqdnOutput = & setspn -Q "HTTP/$fqdn" 2>&1
$spnFqdnOutput | ForEach-Object { Write-Host " $_" }
}
}
catch {
Write-Host " [ERROR] Could not run setspn: $_" -ForegroundColor Red
}
Write-Host ""
}
# 6. Test HTTP call with Negotiate auth and detect protocol
Write-Host "--- HTTP Authentication Test ---" -ForegroundColor Green
Write-Host " Testing: $Url" -ForegroundColor Yellow
Write-Host ""
try {
# Step 1: Check server authentication challenge (no credentials)
Write-Host " Step 1: Checking server authentication challenge (no credentials)..." -ForegroundColor Yellow
try {
$challengeResponse = Invoke-WebRequest -Uri $Url -Method Get -UseBasicParsing `
-ErrorAction SilentlyContinue -SkipHttpErrorCheck
if ($challengeResponse.StatusCode -eq 401) {
$wwwAuth = $challengeResponse.Headers['WWW-Authenticate']
Write-Host " Server WWW-Authenticate: $wwwAuth"
if ($wwwAuth -match 'Negotiate') { Write-Host " [OK] Server supports Negotiate (Kerberos preferred)" -ForegroundColor Green }
if ($wwwAuth -match 'NTLM') { Write-Host " [WARN] Server also advertises NTLM - fallback is possible" -ForegroundColor Yellow }
}
else {
Write-Host " Server returned $($challengeResponse.StatusCode) without requiring auth" -ForegroundColor Yellow
}
}
catch { Write-Host " Could not get challenge response: $_" -ForegroundColor Yellow }
Write-Host ""
# Step 2: Test with Default Credentials (Negotiate)
Write-Host " Step 2: Testing with Default Credentials (Negotiate)..." -ForegroundColor Yellow
$handler = [System.Net.Http.HttpClientHandler]::new()
$handler.UseDefaultCredentials = $true
$handler.PreAuthenticate = $true
$handler.ServerCertificateCustomValidationCallback = [System.Net.Http.HttpClientHandler]::DangerousAcceptAnyServerCertificateValidator
$client = [System.Net.Http.HttpClient]::new($handler)
$client.Timeout = [TimeSpan]::FromSeconds(30)
$stopwatch = [System.Diagnostics.Stopwatch]::StartNew()
$response = $client.GetAsync($Url).GetAwaiter().GetResult()
$stopwatch.Stop()
Write-Host " Status Code : $($response.StatusCode)" -ForegroundColor $(if ($response.IsSuccessStatusCode) { 'Green' } else { 'Red' })
Write-Host " Response Time : $($stopwatch.ElapsedMilliseconds) ms"
$client.Dispose(); $handler.Dispose()
Write-Host ""
# Step 3: Check for Kerberos ticket after request
Write-Host " Step 3: Checking for Kerberos ticket after request..." -ForegroundColor Yellow
$klistAfter = & klist 2>&1
$httpTicketsAfter = $klistAfter | Select-String -Pattern "HTTP/$targetHost" -Context 1, 1
if ($httpTicketsAfter) {
Write-Host " [OK] Kerberos ticket found for HTTP/$targetHost — KERBEROS IS BEING USED" -ForegroundColor Green
foreach ($ticket in $httpTicketsAfter) { Write-Host " $($ticket.Line.Trim())" }
}
else {
Write-Host " [CRITICAL] No Kerberos ticket for HTTP/$targetHost" -ForegroundColor Red
Write-Host " Authentication likely fell back to NTLM!" -ForegroundColor Red
Write-Host " This causes LSA SID lookup floods on every request," -ForegroundColor Red
Write-Host " resulting in 4+ second response times." -ForegroundColor Red
}
}
catch {
Write-Host " [ERROR] HTTP test failed: $_" -ForegroundColor Red
}
Write-Host ""
# 7. Check Browser Intranet Zone settings
Write-Host "--- Browser Intranet Zone Check ---" -ForegroundColor Green
Write-Host " Checking if $targetHost is in the Local Intranet Zone..." -ForegroundColor Yellow
$intranetZoneKeys = @(
"HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\ZoneMap\Domains\$targetHost",
"HKLM:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\ZoneMap\Domains\$targetHost",
"HKCU:\Software\Policies\Microsoft\Windows\CurrentVersion\Internet Settings\ZoneMap\Domains\$targetHost",
"HKLM:\Software\Policies\Microsoft\Windows\CurrentVersion\Internet Settings\ZoneMap\Domains\$targetHost"
)
$hostParts = $targetHost.Split('.')
if ($hostParts.Length -ge 2) {
$domain = ($hostParts | Select-Object -Skip 1) -join '.'
$intranetZoneKeys += @(
"HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\ZoneMap\Domains\$domain",
"HKLM:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\ZoneMap\Domains\$domain",
"HKCU:\Software\Policies\Microsoft\Windows\CurrentVersion\Internet Settings\ZoneMap\Domains\$domain",
"HKLM:\Software\Policies\Microsoft\Windows\CurrentVersion\Internet Settings\ZoneMap\Domains\$domain"
)
}
$foundInZone = $false
foreach ($key in $intranetZoneKeys) {
if (Test-Path $key) {
$props = Get-ItemProperty -Path $key -ErrorAction SilentlyContinue
Write-Host " Found zone entry: $key" -ForegroundColor Yellow
$props.PSObject.Properties | Where-Object { $_.Name -notlike 'PS*' } | ForEach-Object {
$zoneName = switch ($_.Value) { 1 { "Intranet" } 2 { "Trusted" } 3 { "Internet" } 4 { "Restricted" } default { "Unknown" } }
Write-Host " $($_.Name) = $($_.Value) ($zoneName)"
if ($_.Value -eq 1) { $foundInZone = $true }
}
}
}
$autoDetectKey = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\ZoneMap"
if (Test-Path $autoDetectKey) {
$autoDetect = Get-ItemProperty -Path $autoDetectKey -ErrorAction SilentlyContinue
Write-Host " Auto-detect intranet: IntranetName=$($autoDetect.IntranetName), ProxyOverride=$($autoDetect.ProxyOverride)"
}
if (-not $foundInZone) {
Write-Host " [WARN] $targetHost is NOT explicitly in the Local Intranet Zone." -ForegroundColor Yellow
Write-Host " Edge/Chrome will use NTLM unless the site is in the Intranet Zone." -ForegroundColor Yellow
Write-Host ""
Write-Host " REMEDIATION:" -ForegroundColor Cyan
Write-Host " Option A: Group Policy > Site to Zone Assignment List" -ForegroundColor Cyan
Write-Host " Add: $($uri.Scheme)://$targetHost = 1 (Intranet)" -ForegroundColor Cyan
Write-Host " Option B: Registry: HKLM:\Software\...\ZoneMap\Domains\$domain" -ForegroundColor Cyan
Write-Host " Option C: Manual: Internet Options > Security > Local Intranet > Sites > Advanced" -ForegroundColor Cyan
}
else {
Write-Host " [OK] $targetHost is in the Local Intranet Zone." -ForegroundColor Green
}
Write-Host ""
# 8. Check Edge/Chrome auth policies
Write-Host "--- Edge/Chrome Authentication Policies ---" -ForegroundColor Green
$edgePolicies = @(
@{ Path = "HKLM:\SOFTWARE\Policies\Microsoft\Edge"; Name = "AuthServerAllowlist" },
@{ Path = "HKLM:\SOFTWARE\Policies\Microsoft\Edge"; Name = "AuthNegotiateDelegateAllowlist" },
@{ Path = "HKLM:\SOFTWARE\Policies\Microsoft\Edge"; Name = "AuthSchemes" },
@{ Path = "HKLM:\SOFTWARE\Policies\Google\Chrome"; Name = "AuthServerAllowlist" },
@{ Path = "HKLM:\SOFTWARE\Policies\Google\Chrome"; Name = "AuthNegotiateDelegateAllowlist" },
@{ Path = "HKLM:\SOFTWARE\Policies\Google\Chrome"; Name = "AuthSchemes" }
)
foreach ($policy in $edgePolicies) {
if (Test-Path $policy.Path) {
$val = (Get-ItemProperty -Path $policy.Path -Name $policy.Name -ErrorAction SilentlyContinue).$($policy.Name)
if ($val) { Write-Host " $($policy.Path)\$($policy.Name) = $val" -ForegroundColor Yellow }
}
}
$authServerAllowlist = $null
if (Test-Path "HKLM:\SOFTWARE\Policies\Microsoft\Edge") {
$authServerAllowlist = (Get-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Edge" `
-Name "AuthServerAllowlist" -ErrorAction SilentlyContinue).AuthServerAllowlist
}
if (-not $authServerAllowlist) {
Write-Host " [INFO] Edge AuthServerAllowlist policy is not configured." -ForegroundColor Yellow
Write-Host " Consider setting it to '*.$($env:USERDNSDOMAIN)' to enable Negotiate/Kerberos." -ForegroundColor Yellow
}
Write-Host ""
# 9. Performance test (5 sequential requests)
Write-Host "--- Performance Test (5 sequential requests) ---" -ForegroundColor Green
$handler2 = [System.Net.Http.HttpClientHandler]::new()
$handler2.UseDefaultCredentials = $true
$handler2.PreAuthenticate = $true
$handler2.ServerCertificateCustomValidationCallback = [System.Net.Http.HttpClientHandler]::DangerousAcceptAnyServerCertificateValidator
$client2 = [System.Net.Http.HttpClient]::new($handler2)
$client2.Timeout = [TimeSpan]::FromSeconds(60)
$times = @()
for ($i = 1; $i -le 5; $i++) {
try {
$sw = [System.Diagnostics.Stopwatch]::StartNew()
$resp = $client2.GetAsync($Url).GetAwaiter().GetResult()
$sw.Stop()
$times += $sw.ElapsedMilliseconds
Write-Host " Request $i : $($sw.ElapsedMilliseconds) ms (Status: $($resp.StatusCode))"
}
catch { Write-Host " Request $i : FAILED - $_" -ForegroundColor Red }
}
$client2.Dispose(); $handler2.Dispose()
if ($times.Count -gt 0) {
$avg = ($times | Measure-Object -Average).Average
$max = ($times | Measure-Object -Maximum).Maximum
Write-Host ""
Write-Host " Average: $([math]::Round($avg, 0)) ms, Max: $max ms"
if ($avg -gt 2000) { Write-Host " [CRITICAL] Average > 2s — likely NTLM with LSA flood." -ForegroundColor Red }
elseif ($avg -gt 500) { Write-Host " [WARN] Average > 500ms — may indicate auth overhead." -ForegroundColor Yellow }
else { Write-Host " [OK] Response times look healthy." -ForegroundColor Green }
}
Write-Host ""
# 10. Summary and recommendations
Write-Host "============================================================" -ForegroundColor Cyan
Write-Host " SUMMARY & RECOMMENDATIONS" -ForegroundColor Cyan
Write-Host "============================================================" -ForegroundColor Cyan
Write-Host ""
Write-Host "If authentication is falling back to NTLM:" -ForegroundColor Yellow
Write-Host ""
Write-Host "1. Add the Nodinite URL to the Local Intranet Zone (immediate fix):" -ForegroundColor White
Write-Host " Group Policy > Site to Zone Assignment List" -ForegroundColor White
Write-Host " Value: $($uri.Scheme)://$targetHost = 1" -ForegroundColor White
Write-Host ""
Write-Host "2. Verify SPNs are registered for the IIS service account:" -ForegroundColor White
Write-Host " setspn -S HTTP/$targetHost <ServiceAccountName>" -ForegroundColor White
Write-Host " setspn -S HTTP/$($targetHost):$targetPort <ServiceAccountName>" -ForegroundColor White
Write-Host ""
Write-Host "3. For Edge, configure AuthServerAllowlist policy:" -ForegroundColor White
Write-Host " HKLM\SOFTWARE\Policies\Microsoft\Edge\AuthServerAllowlist = '*.$($env:USERDNSDOMAIN)'" -ForegroundColor White
Write-Host ""
Write-Host "4. Ensure Kerberos delegation is configured if Web API impersonates" -ForegroundColor White
Write-Host " users to backend services (SQL Server, etc.)." -ForegroundColor White
Write-Host ""
Write-Host "5. Consider upgrading to OpenID Connect / SSO to eliminate" -ForegroundColor White
Write-Host " Windows Authentication overhead entirely." -ForegroundColor White
Write-Host "============================================================" -ForegroundColor Cyan
Script Usage
# Basic check - run from the workstation experiencing slow performance
.\kerberoscheck.ps1 -Url "https://nodinite.yourdomain.com/api/isalive"
# Include SPN registration verification (requires domain tools)
.\kerberoscheck.ps1 -Url "https://nodinite.yourdomain.com/api/isalive" -CheckSPN
What the Script Checks
| Check | Purpose |
|---|---|
| DNS resolution | Verifies the hostname resolves correctly |
klist ticket cache |
Checks if Kerberos tickets exist for the target |
| SPN registration | Verifies HTTP/<hostname> SPN is registered (with -CheckSPN) |
| Server challenge | Confirms server advertises Negotiate |
| HTTP auth test | Makes a real HTTP call and detects auth protocol used |
| Intranet Zone registry | Confirms site is in any of the four zone map registry locations |
| Edge/Chrome auth policies | Checks AuthServerAllowlist and AuthSchemes |
| Performance test | Measures 5 sequential requests to quantify degradation |
Diagnose First: Two Independent Gates
Kerberos requires both gates to be open. The diagnostic shortcut is:
| Test | Result | Meaning |
|---|---|---|
curl --negotiate -u : https://nodinite.yourdomain.com/api/isalive is fast |
Gate 2 open | SPN is registered correctly — do not touch SPNs |
| Browser is slow but curl is fast | Gate 1 closed | Browser is not attempting Kerberos — fix zone/policy |
| Both curl and browser are slow | Both gates closed | Fix zone first, then check SPN |
| Browser fast after adding to Intranet Zone | Root cause confirmed | Make zone setting permanent via GPO |
Important
If
curl --negotiatealready works, the SPN is already registered correctly. Do not register new SPNs — you will waste time and risk duplicates. The only fix needed is Gate 1: the browser zone or policy.
SPN registration is a separate concern covered in Service Principal Names (SPN).
Resolution Steps
1. Add Site to Local Intranet Zone (Immediate Fix)
This is the highest-impact, lowest-risk fix. It tells the browser to use Negotiate/Kerberos instead of NTLM.
Via Group Policy (Recommended for Enterprise)
- Open Group Policy Management Console
- Navigate to:
Computer Configuration > Administrative Templates > Windows Components > Internet Explorer > Internet Control Panel > Security Page - Open Site to Zone Assignment List → Enable
- Add entries (Value
1= Local Intranet):
| Setting | Value |
|---|---|
https://nodinite.yourdomain.com |
1 |
https://nodinite.yourdomain.com:40001 |
1 |
- Run
gpupdate /forceon affected machines
Via Registry (Per-Machine, No Reboot Required)
# Run as Administrator on the client workstation
$domain = "nodinite.yourdomain.com" # Replace with your hostname
$regPath = "HKLM:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\ZoneMap\Domains\$domain"
New-Item -Path $regPath -Force
Set-ItemProperty -Path $regPath -Name "https" -Value 1 -Type DWord
Write-Host "✓ $domain added to Local Intranet Zone (Value 1)" -ForegroundColor Green
Manual (Per-User, for Testing Only)
- Open
inetcpl.cpl(Internet Options) - Security tab → Local Intranet → Sites → Advanced
- Add:
https://nodinite.yourdomain.com - Click Close → OK
- Restart browser — Kerberos should now be used
2. Configure Edge/Chrome Authentication Policies
For Edge (Chromium-based), set the AuthServerAllowlist policy to allow Negotiate for your domain:
# Set via PowerShell (Run as Administrator)
New-Item -Path "HKLM:\SOFTWARE\Policies\Microsoft\Edge" -Force
Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Edge" `
-Name "AuthServerAllowlist" -Value "*.yourdomain.com" # Replace with your domain
# Verify
Get-ItemProperty "HKLM:\SOFTWARE\Policies\Microsoft\Edge" -Name "AuthServerAllowlist"
Or deploy via Group Policy:
Computer Configuration > Administrative Templates > Microsoft Edge > HTTP authentication
Policy: Authentication server allowlist
Value: *.yourdomain.com
3. SPN Missing? (Only If Both curl and Browser Are Slow)
If curl --negotiate is also slow or returns a 401, the HTTP SPN for the IIS application pool service account may not be registered. See Service Principal Names (SPN) for the complete guide covering default instances, named instances, clusters, and app pool accounts.
Tip
The SPN guide includes an IIS application pool section with
setspncommands for HTTP SPNs. The same tool — Microsoft Kerberos Configuration Manager — detects missing SPNs automatically.
4. Verify Kerberos Delegation (If Web API Accesses Backend Services)
If the Nodinite Web API impersonates users to access SQL Server or other backend services:
- Open Active Directory Users and Computers
- Find the service account → Properties → Delegation tab
- Select: Trust this user for delegation to specified services only
- Select: Use any authentication protocol (or Kerberos only if all SPNs are registered)
- Add the backend service SPNs (e.g.,
MSSQLSvc/sqlserver.yourdomain.com:1433)
See Trusted for delegation for the full guide.
5. Long-Term: Migrate to OpenID Connect / SSO
The permanent solution is to move from Windows Authentication to OpenID Connect with an identity provider (Microsoft Entra ID, Keycloak, AD FS). This eliminates:
- NTLM/Kerberos negotiation issues and required zone configuration
- LSA SID lookup overhead on every request
- Kerberos delegation complexity
- SPN management burden
- Dependency on Active Directory for authentication
See Install Nodinite v7 - OpenID for setup instructions.
Verification
After applying the fix, verify Kerberos is being used:
# 1. Open a new browser window and navigate to the Nodinite URL
# 2. Check klist for an HTTP service ticket
klist
# Look for a ticket like:
# Server: HTTP/nodinite.yourdomain.com @ YOURDOMAIN.COM
# KerbTicket Encryption Type: AES-256-CTS-HMAC-SHA1-96
# 3. Measure request time (should be <500ms with Kerberos)
Measure-Command {
Invoke-WebRequest -Uri "https://nodinite.yourdomain.com/api/isalive" -UseDefaultCredentials
}
Quick Reference
| Symptom | Cause | Fix |
|---|---|---|
| 4+ second API response times | NTLM causing LSA SID flood | Add site to Intranet Zone |
Hundreds of LookupSids in logs |
SID→Name translation per request | Add site to Intranet Zone |
No HTTP ticket in klist after browser request |
Browser not attempting Kerberos | Intranet Zone + Edge policy |
curl fast, browser slow |
curl uses Negotiate, browser falls back to NTLM | Fix zone/policy only — SPN is fine |
Both curl and browser slow |
SPN likely missing | Check SPN registration — see SPN guide |
| Works after adding to Intranet Zone | Confirms NTLM was the root cause | Make zone setting permanent via GPO |
Next Step
How to perform hardening on your Nodinite installation
Related Topics
- How to perform hardening on your Nodinite installation
- IIS Authentication settings
- Service Principal Names (SPN)
- Trusted for delegation
- Install Nodinite v7 - OpenID
- IIS Windows Features