Skip to content

🚨 Troubleshooting

Objetivo: Identificar, diagnosticar y resolver problemas comunes y complejos con Caddy en entornos de desarrollo.


🚫 Puerto 443/80 Ocupado

Síntomas:

  • Error: bind: address already in use
  • Caddy no puede iniciar

Diagnóstico:

Terminal window
# Identificar qué proceso usa el puerto
netstat -ano | findstr ":443"
netstat -ano | findstr ":80"
# Ver detalles del proceso
tasklist /FI "PID eq [PID_NUMBER]"

Soluciones:

Terminal window
# Opción 1: Terminar proceso específico
taskkill /F /PID [PID_NUMBER]
# Opción 2: Terminar todos los Caddy
Get-Process caddy | Stop-Process -Force
# Opción 3: Si es IIS ocupando puerto 80
net stop http
# Luego reconfigurar IIS en otro puerto

🔒 Problemas de Certificados SSL

Síntomas:

  • certificate verify failed
  • Navegador muestra “No seguro”
  • Error: tls: internal error

Soluciones:

Terminal window
# Regenerar todos los certificados
.\caddy.exe untrust
.\caddy.exe trust
# Limpiar cache de certificados
Remove-Item "$env:LOCALAPPDATA\Caddy\*" -Recurse -Force
# Verificar certificado específico
openssl s_client -connect mi-motor.local:443 -servername mi-motor.local
# Si persiste, verificar hosts file
notepad C:\Windows\System32\drivers\etc\hosts

🌐 DNS No Resuelve

Síntomas:

  • nslookup falla para dominio local
  • Navegador no encuentra el sitio
  • ERR_NAME_NOT_RESOLVED

Soluciones:

Terminal window
# Verificar entrada en hosts
Get-Content C:\Windows\System32\drivers\etc\hosts | Select-String "mi-motor.local"
# Si no existe, agregar:
Add-Content C:\Windows\System32\drivers\etc\hosts "127.0.0.1 mi-motor.local"
# Limpiar cache DNS
ipconfig /flushdns
# Test DNS
nslookup mi-motor.local
ping mi-motor.local

Terminal window
param(
[string]$Domain = "mi-motor.local",
[string]$CaddyPath = "C:\tools\caddy",
[switch]$Verbose
)
$ErrorActionPreference = "Continue"
$results = @()
function Add-Result($Test, $Status, $Message, $Details = "") {
$results += [PSCustomObject]@{
Test = $Test
Status = $Status
Message = $Message
Details = $Details
}
}
function Write-TestResult($Test, $Status, $Message) {
$color = switch ($Status) {
"PASS" { "Green" }
"FAIL" { "Red" }
"WARN" { "Yellow" }
}
$icon = switch ($Status) {
"PASS" { "" }
"FAIL" { "" }
"WARN" { "⚠️" }
}
Write-Host "$icon $Test`: $Message" -ForegroundColor $color
}
Write-Host "🔍 Diagnóstico Completo de Caddy para $Domain" -ForegroundColor Cyan
Write-Host "=" * 60
# 1. Verificar proceso Caddy
try {
$caddyProcess = Get-Process -Name "caddy" -ErrorAction Stop
Add-Result "Proceso Caddy" "PASS" "Ejecutándose (PID: $($caddyProcess.Id))" "Memoria: $([math]::Round($caddyProcess.WorkingSet64/1MB, 2)) MB"
Write-TestResult "Proceso Caddy" "PASS" "Ejecutándose (PID: $($caddyProcess.Id))"
} catch {
Add-Result "Proceso Caddy" "FAIL" "No está ejecutándose" $_.Exception.Message
Write-TestResult "Proceso Caddy" "FAIL" "No está ejecutándose"
}
# 2. Verificar puertos críticos
$ports = @(80, 443)
foreach ($port in $ports) {
try {
$connection = Test-NetConnection -ComputerName "localhost" -Port $port -InformationLevel Quiet -WarningAction SilentlyContinue
if ($connection) {
Add-Result "Puerto $port" "PASS" "Abierto y accesible"
Write-TestResult "Puerto $port" "PASS" "Abierto"
} else {
# Verificar qué proceso usa el puerto
$netstat = netstat -ano | Select-String ":$port "
Add-Result "Puerto $port" "FAIL" "Cerrado o inaccesible" $netstat
Write-TestResult "Puerto $port" "FAIL" "Cerrado"
}
} catch {
Add-Result "Puerto $port" "FAIL" "Error verificando puerto" $_.Exception.Message
Write-TestResult "Puerto $port" "FAIL" "Error en verificación"
}
}
# 3. Verificar resolución DNS
try {
$dns = Resolve-DnsName $Domain -ErrorAction Stop
Add-Result "DNS" "PASS" "Resuelve a $($dns.IPAddress)"
Write-TestResult "DNS" "PASS" "Resuelve correctamente"
} catch {
# Verificar hosts file
$hostsContent = Get-Content "C:\Windows\System32\drivers\etc\hosts" -ErrorAction SilentlyContinue
$hostsEntry = $hostsContent | Select-String $Domain
if ($hostsEntry) {
Add-Result "DNS" "WARN" "No resuelve DNS pero existe en hosts" $hostsEntry
Write-TestResult "DNS" "WARN" "Solo en hosts file"
} else {
Add-Result "DNS" "FAIL" "No resuelve y no está en hosts" $_.Exception.Message
Write-TestResult "DNS" "FAIL" "No configurado"
}
}
# 4. Test conectividad HTTP
try {
$httpResponse = Invoke-WebRequest -Uri "http://$Domain" -UseBasicParsing -TimeoutSec 10 -ErrorAction Stop
Add-Result "HTTP" "PASS" "Responde $($httpResponse.StatusCode)" "Content-Length: $($httpResponse.Content.Length)"
Write-TestResult "HTTP" "PASS" "Responde $($httpResponse.StatusCode)"
} catch {
Add-Result "HTTP" "FAIL" "No responde" $_.Exception.Message
Write-TestResult "HTTP" "FAIL" "No responde"
}
# 5. Test conectividad HTTPS
try {
$httpsResponse = Invoke-WebRequest -Uri "https://$Domain" -UseBasicParsing -TimeoutSec 10 -SkipCertificateCheck -ErrorAction Stop
Add-Result "HTTPS" "PASS" "Responde $($httpsResponse.StatusCode)" "SSL OK"
Write-TestResult "HTTPS" "PASS" "Responde $($httpsResponse.StatusCode)"
} catch {
Add-Result "HTTPS" "FAIL" "No responde" $_.Exception.Message
Write-TestResult "HTTPS" "FAIL" "No responde"
}
# 6. Verificar configuración Caddy
$caddyfile = Join-Path $CaddyPath "Caddyfile"
if (Test-Path $caddyfile) {
try {
$caddyExe = Join-Path $CaddyPath "caddy.exe"
$validation = & $caddyExe validate --config $caddyfile 2>&1
if ($LASTEXITCODE -eq 0) {
Add-Result "Configuración" "PASS" "Sintaxis válida"
Write-TestResult "Configuración" "PASS" "Sintaxis válida"
} else {
Add-Result "Configuración" "FAIL" "Sintaxis inválida" $validation
Write-TestResult "Configuración" "FAIL" "Sintaxis inválida"
}
} catch {
Add-Result "Configuración" "FAIL" "Error validando" $_.Exception.Message
Write-TestResult "Configuración" "FAIL" "Error validando"
}
} else {
Add-Result "Configuración" "FAIL" "Caddyfile no encontrado" $caddyfile
Write-TestResult "Configuración" "FAIL" "Caddyfile no encontrado"
}
# 7. Verificar logs recientes
$logPath = Join-Path $CaddyPath "caddy.log"
if (Test-Path $logPath) {
$recentErrors = Get-Content $logPath -Tail 20 | Select-String -Pattern "error|failed|timeout" -CaseSensitive:$false
if ($recentErrors) {
Add-Result "Logs" "WARN" "Errores recientes encontrados" ($recentErrors -join "`n")
Write-TestResult "Logs" "WARN" "$($recentErrors.Count) errores recientes"
} else {
Add-Result "Logs" "PASS" "Sin errores recientes"
Write-TestResult "Logs" "PASS" "Sin errores recientes"
}
} else {
Add-Result "Logs" "WARN" "Archivo de logs no encontrado" $logPath
Write-TestResult "Logs" "WARN" "Sin archivo de logs"
}
# 8. Test específicos de handlers
$handlers = @(
@{ Name = "Offer Handler"; Path = "/offer" }
@{ Name = "Checkout Handler"; Path = "/chk" }
@{ Name = "Purchase Handler"; Path = "/purchase" }
)
foreach ($handler in $handlers) {
try {
$url = "https://$Domain$($handler.Path)"
$response = Invoke-WebRequest -Uri $url -UseBasicParsing -TimeoutSec 5 -SkipCertificateCheck -ErrorAction Stop
Add-Result $handler.Name "PASS" "Responde $($response.StatusCode)"
Write-TestResult $handler.Name "PASS" "OK"
} catch {
if ($_.Exception.Message -like "*404*") {
Add-Result $handler.Name "WARN" "Handler no configurado (404)"
Write-TestResult $handler.Name "WARN" "No configurado"
} else {
Add-Result $handler.Name "FAIL" "Error de conexión" $_.Exception.Message
Write-TestResult $handler.Name "FAIL" "Error"
}
}
}
# Resumen final
Write-Host "`n" + "=" * 60
$passCount = ($results | Where-Object { $_.Status -eq "PASS" }).Count
$failCount = ($results | Where-Object { $_.Status -eq "FAIL" }).Count
$warnCount = ($results | Where-Object { $_.Status -eq "WARN" }).Count
Write-Host "📊 Resumen:" -ForegroundColor Cyan
Write-Host " ✅ Exitosos: $passCount" -ForegroundColor Green
Write-Host " ❌ Fallidos: $failCount" -ForegroundColor Red
Write-Host " ⚠️ Advertencias: $warnCount" -ForegroundColor Yellow
if ($failCount -eq 0) {
Write-Host "`n🎉 ¡Sistema funcionando correctamente!" -ForegroundColor Green
} elseif ($failCount -le 2) {
Write-Host "`n⚠️ Sistema con problemas menores" -ForegroundColor Yellow
} else {
Write-Host "`n🚨 Sistema con problemas críticos" -ForegroundColor Red
}
if ($Verbose) {
Write-Host "`n📋 Detalles completos:" -ForegroundColor Cyan
$results | Format-Table Test, Status, Message, Details -AutoSize
}
Write-Host "`n🏁 Diagnóstico completado" -ForegroundColor Cyan
return $results

Error: Caddyfile:X: unrecognized directive

Terminal window
# ❌ Problema
mi-motor.local {
invalid_directive value # ← Directiva inexistente
}
# ✅ Solución
mi-motor.local {
reverse_proxy localhost:80
}

Error: parsing caddyfile tokens: too many open blocks

Terminal window
# ❌ Problema - llaves no balanceadas
mi-motor.local {
handle /offer {
reverse_proxy localhost:5250
# ← Falta cerrar llave
}
# ✅ Solución
mi-motor.local {
handle /offer {
reverse_proxy localhost:5250
}
}

Error: site block may only contain a hostname

Terminal window
# ❌ Problema
mi-motor.local/path { # ← No se permite path en site block
reverse_proxy localhost:80
}
# ✅ Solución
mi-motor.local {
handle /path {
reverse_proxy localhost:80
}
}

Error: upstream connect error or disconnect/reset

Terminal window
# Problema: Backend no responde
# Diagnóstico
Test-NetConnection localhost -Port 5250
# Solución: Aumentar timeouts
reverse_proxy localhost:5250 {
timeout 30s
dial_timeout 10s
read_timeout 30s
write_timeout 30s
}

Error: context deadline exceeded

Terminal window
# Solución: Configurar timeouts específicos por handler
handle /offer {
reverse_proxy test-landings-api.XXXXX.com {
timeout 60s # ← Timeout total
dial_timeout 15s # ← Tiempo para conectar
header_up Host test-landings-api.XXXXX.com
}
}

Optimizar performance

Terminal window
{
# Configuración global para performance
admin off # ← Deshabilitar admin API si no se usa
auto_https disable_redirects
# Limits globales
servers {
max_header_size 16KB
read_timeout 30s
write_timeout 30s
idle_timeout 120s
}
}
mi-motor.local {
# Compresión para responses grandes
encode gzip
# Cache headers para assets estáticos
@static path *.css *.js *.png *.jpg *.ico
header @static Cache-Control "public, max-age=86400"
reverse_proxy localhost:80 {
# Connection pooling
keepalive 2m
keepalive_idle_conns 10
}
}