louislam/uptime-kuma · error · Error

Invalid url protocol, only http and https are allowed.

Error message

Invalid url protocol, only http and https are allowed.

What it means

Before navigating, the monitor constructs new URL(monitor.url) and rejects any protocol other than http: or https:. This is an explicit Local File Inclusion (LFI) mitigation (GHSA-2qgm-m29m-cj2h): without it, a monitor URL like file:///etc/passwd could be opened by Chromium. The throw at real-browser-monitor-type.js:264 is the gate.

Source

Thrown at server/monitor-types/real-browser-monitor-type.js:264

class RealBrowserMonitorType extends MonitorType {
    name = "real-browser";

    /**
     * @inheritdoc
     */
    async check(monitor, heartbeat, server) {
        const browser = monitor.remote_browser
            ? await getRemoteBrowser(monitor.remote_browser, monitor.user_id)
            : await getBrowser();
        const context = await browser.newContext();
        const page = await context.newPage();

        // Prevent Local File Inclusion
        // Accept only http:// and https://
        // https://github.com/louislam/uptime-kuma/security/advisories/GHSA-2qgm-m29m-cj2h
        let url = new URL(monitor.url);
        if (url.protocol !== "http:" && url.protocol !== "https:") {
            throw new Error("Invalid url protocol, only http and https are allowed.");
        }

        const res = await page.goto(monitor.url, {
            waitUntil: "networkidle",
            timeout: monitor.interval * 1000 * 0.8,
        });

        // Wait for additional time before taking screenshot if configured
        if (monitor.screenshot_delay > 0) {
            await page.waitForTimeout(monitor.screenshot_delay);
        }

        let filename = jwt.sign(monitor.id, server.jwtSecret) + ".png";

        await page.screenshot({
            path: path.join(Database.screenshotDir, filename),
        });

View on GitHub (pinned to 6b5ea01557)

Solutions

  1. Use only http:// or https:// URLs in the monitor's url field.
  2. For internal/local pages, serve them over a local HTTP server and point the monitor at that.
  3. Strip schemes client-side before saving if integrating programmatically.
  4. Treat any non-http(s) input as invalid in upstream form validation.

Example fix

// before
monitor.url = "file:///var/www/index.html";
// after
monitor.url = "http://127.0.0.1:8080/index.html";
Defensive patterns

Strategy: validation

Validate before calling

function validateMonitorUrl(url) {
  const u = new URL(url);              // rejects malformed URLs
  if (u.protocol !== 'http:' && u.protocol !== 'https:') {
    throw new Error(`Disallowed protocol ${u.protocol}; only http/https allowed`);
  }
  return u.toString();
}

Type guard

function isHttpUrl(s) { try { const u = new URL(s); return u.protocol === 'http:' || u.protocol === 'https:'; } catch { return false; } }

Try / catch

try {
  await realBrowserMonitor.check(monitor, heartbeat, server);
} catch (e) {
  if (/Invalid url protocol/.test(e.message)) {
    // reject the monitor URL at the form layer; never auto-rewrite schemes
  }
  throw e;
}

Prevention

When it happens

Trigger: monitor.url uses a non-http(s) scheme: file://, ftp://, data:, chrome://, javascript:, or a custom protocol handler. Any of these trips the protocol check immediately.

Common situations: User enters a file:// path expecting a local screenshot, a copy-pasted data: URL, or a mis-typed scheme. Also an adversarial input attempting LFI through the monitor.

Related errors


AI-assisted analysis of louislam/uptime-kuma@6b5ea01557 (2026-08-12). Data as JSON: /api/errors/4a2675bd4e3c12c2. Report an issue: GitHub.