MagicMirrorOrg/MagicMirror · warning

Forbidden: domain not in corsDomainWhitelist

Error message

Forbidden: domain not in corsDomainWhitelist

What it means

When MagicMirror's `cors` config is set to "allowWhitelist", the /cors proxy only forwards requests whose target hostname appears in the `corsDomainWhitelist` array (compared lowercase). Any other domain is refused with 403 'domain not in corsDomainWhitelist'. This is an intentional configuration gate, not a network failure.

Source

Thrown at js/server_functions.js:101

			} catch {
				Log.warn(`SSRF blocked (invalid URL): ${url}`);
				return res.status(403).json({ error: "Forbidden: private or reserved addresses are not allowed" });
			}
			if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
				Log.warn(`SSRF blocked (protocol): ${url}`);
				return res.status(403).json({ error: "Forbidden: private or reserved addresses are not allowed" });
			}

			// Block localhost by hostname before even creating the dispatcher (no DNS needed).
			if (parsed.hostname.toLowerCase() === "localhost") {
				Log.warn(`SSRF blocked (localhost): ${url}`);
				return res.status(403).json({ error: "Forbidden: private or reserved addresses are not allowed" });
			}

			// Whitelist check: if enabled, only allow explicitly listed domains
			if (global.config.cors === "allowWhitelist" && !global.config.corsDomainWhitelist.includes(parsed.hostname.toLowerCase())) {
				Log.warn(`CORS blocked (not in whitelist): ${url}`);
				return res.status(403).json({ error: "Forbidden: domain not in corsDomainWhitelist" });
			}

			const headersToSend = getHeadersToSend(req.url);
			const expectedReceivedHeaders = geExpectedReceivedHeaders(req.url);
			Log.log(`cors url: ${url}`);

			// Resolve DNS once and validate the IP. The validated IP is then pinned
			// for the actual connection so fetch() cannot re-resolve to a different
			// address. This prevents DNS rebinding / TOCTOU attacks (GHSA-xhvw-r95j-xm4v).
			const { address, family } = await dns.promises.lookup(parsed.hostname);
			if (ipaddr.process(address).range() !== "unicast") {
				Log.warn(`SSRF blocked: ${url}`);
				return res.status(403).json({ error: "Forbidden: private or reserved addresses are not allowed" });
			}

			// Pin the validated IP — fetch() reuses it instead of doing its own DNS lookup
			const dispatcher = new undici.Agent({
				connect: {

View on GitHub (pinned to 4b4a59534f)

Solutions

  1. Add the exact bare hostname to corsDomainWhitelist in config.js, e.g. corsDomainWhitelist: ["example.com"]
  2. Whitelist entries are hostname-only and matched after toLowerCase — do not include scheme, path, or uppercase letters
  3. Alternatively set cors: "allowAll" in config.js if you accept the security tradeoff
  4. If the target legitimately redirects to another host, whitelist the redirected hostname too

Example fix

// before (config.js)
cors: "allowWhitelist",
corsDomainWhitelist: ["https://calendar.example.com/feed.ics"]
// after
cors: "allowWhitelist",
corsDomainWhitelist: ["calendar.example.com"]
Defensive patterns

Strategy: validation

Validate before calling

const host = new URL(feedUrl).hostname.toLowerCase();
if (window.MM_CONFIG_whitelist && !window.MM_CONFIG_whitelist.includes(host)) {
  console.warn(`${host} missing from corsDomainWhitelist in config.js`);
}

Type guard

function isWhitelisted(u, whitelist) {
  try { return whitelist.map(h => h.toLowerCase()).includes(new URL(u).hostname.toLowerCase()); } catch { return false; }
}

Try / catch

const res = await fetch(proxyUrl);
if (res.status === 403 && (await res.json()).error.includes("corsDomainWhitelist")) {
  throw new Error(`Add ${new URL(feedUrl).hostname} to corsDomainWhitelist in config.js`);
}

Prevention

When it happens

Trigger: config.js contains `cors: "allowWhitelist"` and a module requests GET /cors?url=https://some-domain.com/... where some-domain.com (lowercased hostname) is not an entry of `corsDomainWhitelist`.

Common situations: Switching from default/allowAll to allowWhitelist without updating the whitelist; adding 'https://example.com/path' (full URL) or 'WWW.Example.com' (case) to the whitelist instead of the bare lowercase hostname 'example.com'; a feed changing domains (e.g. redirect to a CDN).

Understand the failure class

Related errors


AI-assisted analysis of MagicMirrorOrg/MagicMirror@4b4a59534f (2026-08-31). Data as JSON: /api/errors/2e120b8e9aa383a3. Report an issue: GitHub.