TLS/HTTPS Troubleshooting for Nodinite v7
Common issues encountered after enabling TLS/HTTPS on Nodinite v7 installations. For the main hardening guide, see How to perform hardening.
Quick Navigation
Slow Page Loads: Certificate Revocation Timeout (CRL/OCSP)
Problem: Browser and Swagger UI take 20-30 seconds to load every page, but curl and API calls from PowerShell are fast. The delay is consistent on every request, not just the first.
Root Cause: Your certificate includes CRL (Certificate Revocation List) or OCSP (Online Certificate Status Protocol) URLs that browsers try to contact before rendering each page. If the IIS server or client workstations cannot reach these URLs (blocked firewall, unreachable CA), browsers wait for the full timeout before proceeding. curl is unaffected because it does not check certificate revocation by default.
This issue is particularly common with:
- Let's Encrypt certificates (CRL/OCSP URLs point to
r12.c.lencr.org,r13.i.lencr.org) - Internal CA certificates where the CRL distribution point is not accessible from the internet or client workstations
- Air-gapped or restricted-network environments
Diagnose: Check if Your Certificate Has CRL/OCSP URLs
Run this PowerShell script on the IIS server to inspect the certificate and test reachability of revocation endpoints:
#Requires -Version 7.0
# Diagnose CRL/OCSP certificate revocation timeout
# Replace the Subject filter with your actual certificate subject/hostname
$cert = Get-ChildItem -Path Cert:\LocalMachine\My |
Where-Object { $_.Subject -match "nodinite\.yourdomain\.com" } | # Replace with your hostname
Sort-Object NotBefore -Descending |
Select-Object -First 1
if (-not $cert) {
Write-Host "❌ Certificate not found - check subject name" -ForegroundColor Red
Write-Host "Available certificates:" -ForegroundColor Yellow
Get-ChildItem Cert:\LocalMachine\My | Select-Object Subject, Thumbprint, NotAfter
exit 1
}
Write-Host "=== Certificate Information ===" -ForegroundColor Cyan
Write-Host "Subject : $($cert.Subject)" -ForegroundColor White
Write-Host "Issuer : $($cert.Issuer)" -ForegroundColor White
Write-Host "Thumbprint : $($cert.Thumbprint)" -ForegroundColor White
Write-Host "Valid To : $($cert.NotAfter)" -ForegroundColor White
if ($cert.Issuer -eq $cert.Subject) {
Write-Host "Type : Self-signed" -ForegroundColor Green
} else {
Write-Host "Type : CA-issued ($($cert.Issuer))" -ForegroundColor Yellow
}
# Check for CRL Distribution Points
Write-Host "`n=== CRL Distribution Points ===" -ForegroundColor Cyan
$crlExt = $cert.Extensions | Where-Object { $_.Oid.FriendlyName -eq "CRL Distribution Points" }
if ($crlExt) {
Write-Host "❌ CRL URLs found (may cause 20-30s browser timeout if unreachable):" -ForegroundColor Red
$crlText = $crlExt.Format($false)
$crlText
# Extract and test each URL
[regex]::Matches($crlText, 'http[^\s]+') | ForEach-Object {
$url = $_.Value.TrimEnd(')')
Write-Host "`nTesting: $url" -ForegroundColor Yellow
$t = Measure-Command {
try { Invoke-WebRequest -Uri $url -UseBasicParsing -TimeoutSec 5 -ErrorAction Stop | Out-Null; Write-Host " ✓ Reachable" -ForegroundColor Green }
catch { Write-Host " ❌ UNREACHABLE - THIS CAUSES THE 20-30s TIMEOUT!" -ForegroundColor Red }
}
Write-Host " Response time: $([math]::Round($t.TotalSeconds,1))s" -ForegroundColor $(if ($t.TotalSeconds -lt 3) { "Green" } else { "Red" })
}
} else {
Write-Host "✓ No CRL Distribution Points (no revocation URL to time out on)" -ForegroundColor Green
}
# Check for OCSP (Authority Information Access)
Write-Host "`n=== OCSP / Authority Information Access ===" -ForegroundColor Cyan
$aiaExt = $cert.Extensions | Where-Object { $_.Oid.FriendlyName -eq "Authority Information Access" }
if ($aiaExt) {
Write-Host "❌ OCSP/AIA URLs found (may cause browser timeout if unreachable):" -ForegroundColor Red
$aiaText = $aiaExt.Format($false)
$aiaText
[regex]::Matches($aiaText, 'http[^\s]+') | ForEach-Object {
$url = $_.Value.TrimEnd(')')
Write-Host "`nTesting: $url" -ForegroundColor Yellow
$t = Measure-Command {
try { Invoke-WebRequest -Uri $url -UseBasicParsing -TimeoutSec 5 -ErrorAction Stop | Out-Null; Write-Host " ✓ Reachable" -ForegroundColor Green }
catch { Write-Host " ❌ UNREACHABLE - THIS CAUSES THE 20-30s TIMEOUT!" -ForegroundColor Red }
}
Write-Host " Response time: $([math]::Round($t.TotalSeconds,1))s" -ForegroundColor $(if ($t.TotalSeconds -lt 3) { "Green" } else { "Red" })
}
} else {
Write-Host "✓ No OCSP endpoints (no revocation URL to time out on)" -ForegroundColor Green
}
# Summary
Write-Host "`n=== Diagnosis ===" -ForegroundColor Cyan
if ($crlExt -or $aiaExt) {
Write-Host "Certificate has revocation URLs. If any show UNREACHABLE above:" -ForegroundColor Yellow
Write-Host " • curl is fast → curl does not check CRL/OCSP by default" -ForegroundColor White
Write-Host " • Browsers slow → browsers validate revocation on every page load" -ForegroundColor White
Write-Host " • Swagger slow → Swagger uses the browser engine (same behaviour)" -ForegroundColor White
} else {
Write-Host "✓ Certificate is clean - no revocation URLs. Slow loads have a different cause." -ForegroundColor Green
}
Solutions
Option 1: Enable OCSP Stapling on IIS (Recommended)
OCSP stapling makes the IIS server fetch and cache the revocation response, serving it directly to browsers so they never need to contact the CA's OCSP server themselves. This requires the IIS server to be able to reach the CRL/OCSP endpoints (ensure outbound HTTP/port 80 is allowed from IIS to the CA servers).
# Enable OCSP stapling (IIS 10 / Windows Server 2016+)
New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL" `
-Name "EnableOcspStaplingForSni" -Value 1 -PropertyType DWord -Force
# Apply by restarting IIS (faster than restarting HTTP.sys)
iisreset /noforce
# Verify stapling is active using curl
curl.exe -v https://nodinite.yourdomain.com 2>&1 | Select-String "OCSP"
# Look for: OCSP Response Status: successful
Option 2: Use a Self-Signed Certificate Without Revocation URLs
New-SelfSignedCertificate in PowerShell does not embed CRL/OCSP URLs by default, making it ideal for development and isolated environments:
# Generate clean self-signed certificate (no CRL/OCSP URLs)
$cert = New-SelfSignedCertificate `
-DnsName "nodinite.yourdomain.com" ` # Replace with your hostname(s)
-CertStoreLocation "Cert:\LocalMachine\My" `
-FriendlyName "Nodinite HTTPS" `
-NotAfter (Get-Date).AddYears(2) `
-KeyAlgorithm RSA -KeyLength 2048 `
-HashAlgorithm SHA256 `
-Provider "Microsoft RSA SChannel Cryptographic Provider"
Write-Host "Thumbprint: $($cert.Thumbprint)" -ForegroundColor Green
# Add to Trusted Root so browsers trust it
$store = [System.Security.Cryptography.X509Certificates.X509Store]::new("Root","LocalMachine")
$store.Open("ReadWrite"); $store.Add($cert); $store.Close()
Write-Host "Certificate trusted in Local Machine Root store" -ForegroundColor Green
# Update the IIS binding and Nodinite Portal TLS thumbprint to use this certificate
Tip
After generating the certificate, update the certificate thumbprint in the Nodinite Portal under Environment → TLS and re-run the installation script so IIS uses the new certificate.
Option 3: Open Outbound Firewall Access to CA Revocation Servers
If you must use a CA-issued certificate (e.g., Let's Encrypt or internal PKI), ensure outbound HTTP (port 80) is allowed from the IIS server to the CRL/OCSP hostnames listed in the diagnostic script output above.
# Example: allow outbound HTTP for Let's Encrypt CRL/OCSP
New-NetFirewallRule -DisplayName "Allow Let's Encrypt CRL/OCSP" `
-Direction Outbound -Protocol TCP -RemotePort 80,443 `
-Action Allow -Enabled True -Profile Any
Then verify connectivity and re-run the diagnostic script to confirm all revocation URLs are reachable.
Note
Why does curl not show this problem? curl does not check certificate revocation by default. Use
--ssl-revoke-best-effortwith curl to simulate browser behaviour. Downloads in browsers are also typically unaffected — revocation checks only apply to full HTTPS page navigations (not file downloads or API calls from non-browser clients).
401 Unauthorized When Accessing via Hostname Locally
Problem: PowerShell installation script or local browser access via FQDN (e.g., https://nodinitedev28.test.acme.com) returns 401 Unauthorized when accessing from the IIS server itself, but works from remote machines.
Root Cause: Windows loopback security check blocks local access to IIS via hostname when using Windows Authentication. By default, Windows treats local requests using a hostname (not localhost or machine name) as potential security risks.
When This Issue Occurs
The loopback security check is enabled by default on ALL Windows installations. Most Nodinite deployments never encounter this because:
- Remote access pattern — Users access Nodinite from workstations, not from the IIS server itself
- localhost/IP bypass — The check doesn't apply to
https://localhostorhttps://127.0.0.1 - NetBIOS tolerance — Accessing via server name (e.g.,
https://SERVERNAME) may work - Remote installation — Setup scripts typically run from an admin workstation, not on the IIS server
All of these conditions must be present simultaneously for the issue to appear:
- ✅ Script or browser running locally on the IIS server (not remote)
- ✅ Accessing via FQDN (e.g.,
nodinitedev28.test.acme.com) not localhost/IP/machine name - ✅ Windows integrated authentication enabled
- ✅ Typically during installation when PowerShell verifies HTTPS bindings immediately after configuration
Solution 1: Disable Loopback Check (Quick Fix — Less Secure)
Run this PowerShell command as Administrator on the IIS server:
New-ItemProperty HKLM:\System\CurrentControlSet\Control\Lsa -Name "DisableLoopbackCheck" -Value "1" -PropertyType dword
Restart required: No — takes effect immediately for new connections.
Warning
Security Impact: Disabling loopback check applies to all hostnames on the server, reducing security. Use Solution 2 for production environments.
Solution 2: Whitelist Specific Hostnames (Recommended — More Secure)
Note
Already have BackConnectionHostNames configured? Many enterprise environments already use this registry key for other applications (SharePoint, SQL Server Reporting Services, custom web apps). The script below safely adds your Nodinite hostname to existing entries without creating duplicates.
Whitelist only the specific hostname(s) you need:
# Safe script - prevents duplicates
$regPath = "HKLM:\System\CurrentControlSet\Control\Lsa\MSV1_0"
$newHostname = "nodinitedev28.test.acme.com" # Replace with your FQDN
# Create registry path if it doesn't exist
if (-not (Test-Path $regPath)) {
New-Item -Path $regPath -Force | Out-Null
}
# Get existing values (or empty array if not exists)
$existingValues = @()
try {
$existingValues = (Get-ItemProperty -Path $regPath -Name "BackConnectionHostNames" -ErrorAction Stop).BackConnectionHostNames
Write-Host "Found existing BackConnectionHostNames: $($existingValues -join ', ')" -ForegroundColor Yellow
} catch {
Write-Host "BackConnectionHostNames doesn't exist yet - will create it" -ForegroundColor Yellow
}
# Add new hostname only if not already present
if ($existingValues -notcontains $newHostname) {
$updatedValues = $existingValues + $newHostname
Set-ItemProperty -Path $regPath -Name "BackConnectionHostNames" -Value $updatedValues -Force
Write-Host "✓ Added $newHostname to BackConnectionHostNames" -ForegroundColor Green
} else {
Write-Host "✓ $newHostname already exists in BackConnectionHostNames - no changes needed" -ForegroundColor Cyan
}
# Display final result
Write-Host "`nCurrent BackConnectionHostNames:" -ForegroundColor Yellow
(Get-ItemProperty -Path $regPath -Name "BackConnectionHostNames").BackConnectionHostNames | ForEach-Object { Write-Host " - $_" -ForegroundColor White }
Restart required: No — takes effect immediately for new connections.
If you previously ran a script that created duplicate entries, clean them up first:
# Remove duplicates from BackConnectionHostNames
$regPath = "HKLM:\System\CurrentControlSet\Control\Lsa\MSV1_0"
# Get current values
$currentValues = (Get-ItemProperty -Path $regPath -Name "BackConnectionHostNames").BackConnectionHostNames
# Remove duplicates while preserving order
$uniqueValues = $currentValues | Select-Object -Unique
# Update registry with unique values only
Set-ItemProperty -Path $regPath -Name "BackConnectionHostNames" -Value $uniqueValues
Write-Host "✓ Cleaned up duplicates" -ForegroundColor Green
Write-Host "`nCurrent BackConnectionHostNames:" -ForegroundColor Yellow
(Get-ItemProperty -Path $regPath -Name "BackConnectionHostNames").BackConnectionHostNames | ForEach-Object { Write-Host " - $_" -ForegroundColor White }
Verification
- Close all browser windows
- Open new browser and navigate to
https://nodinitedev28.test.acme.com(your FQDN) - You should now successfully authenticate and access Nodinite Web Client
Tip
For production environments, use Solution 2 (BackConnectionHostNames) to limit the security exception to only your Nodinite hostname.
Next Steps
- How to perform hardening — Main HTTPS/TLS configuration guide for Nodinite v7
Related Topics
- Development Certificates — Self-signed TLS for development/test environments
- IIS Authentication settings
- Install Nodinite v7