DC-044 Add X-DashCaddy-HealthCheck marker to batch probe endpoint
CI / Test & Lint (push) Has been cancelled
CI / Security audit (push) Has been cancelled

The dashboard polls /api/v1/services/status (not /probe/:id) for its
refresh loop. routes/services.js's requestStatusCode() didn't set the
X-DashCaddy-HealthCheck: 1 marker, so the batch endpoint hit the
forward_auth gate, got rate-limited by authLimiter (429), and reported
7 services (router, chat, sync, torrent, sonarr, radarr, prowlarr,
requests) as down.

Same fix in src/app.js /probe/:id (the single-service endpoint) for
consistency.

Without the marker, every probe from the container IP trips
authLimiter within 20 requests and the rest of the batch fails.
health-checker.js background poll was already setting the marker
correctly, which is why the cached health view showed 15/15 while
the live dashboard showed 8/15.
This commit is contained in:
Krystie
2026-07-09 02:02:45 -07:00
parent 0f04bb3638
commit 81f6049ded
2 changed files with 36 additions and 7 deletions
+21 -5
View File
@@ -799,14 +799,22 @@ async function createApp() {
const isHttps = parsed.protocol === 'https:';
const lib = isHttps ? https : require('http');
// X-DashCaddy-HealthCheck: 1 — Caddy's (dashcaddy_auth) block matches
// this header (from local container IPs) to bypass the forward_auth gate.
// Without it, every probe hits authLimiter → 429 → marked TIMEOUT.
// See /etc/caddy/Caddyfile (dashcaddy_auth) and the matching logic in
// src/monitoring/health-checker.js (which sets the same marker).
const options = {
hostname: parsed.hostname,
port: parsed.port || (isHttps ? 443 : 80),
path: parsed.pathname + parsed.search,
method: 'HEAD',
timeout: 5000,
timeout: 8000,
agent: isHttps ? httpsAgent : undefined,
headers: { 'User-Agent': APP.USER_AGENTS.PROBE },
headers: {
'User-Agent': APP.USER_AGENTS.PROBE,
'X-DashCaddy-HealthCheck': '1',
},
};
const makeRequest = (method) => new Promise((resolve, reject) => {
@@ -832,7 +840,12 @@ async function createApp() {
if (pylonConfig?.url) {
try {
const pylonUrl = `${pylonConfig.url}/probe?url=${encodeURIComponent(url)}`;
const headers = { 'User-Agent': APP.USER_AGENTS.PROBE };
// Forward healthcheck marker to the remote pylon relay in case its Caddy
// is configured to bypass forward_auth on the same header.
const headers = {
'User-Agent': APP.USER_AGENTS.PROBE,
'X-DashCaddy-HealthCheck': '1',
};
if (pylonConfig.key) headers['x-pylon-key'] = pylonConfig.key;
const controller = new AbortController();
const pylonTimeout = setTimeout(() => controller.abort(), 8000);
@@ -857,9 +870,12 @@ async function createApp() {
port: 443,
path: '/',
method: 'GET',
timeout: 5000,
timeout: 8000,
agent: httpsAgent,
headers: { 'User-Agent': APP.USER_AGENTS.PROBE }
headers: {
'User-Agent': APP.USER_AGENTS.PROBE,
'X-DashCaddy-HealthCheck': '1',
}
}, (fRes) => {
fRes.resume();
resolve(fRes.statusCode);