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
- Use only http:// or https:// URLs in the monitor's url field.
- For internal/local pages, serve them over a local HTTP server and point the monitor at that.
- Strip schemes client-side before saving if integrating programmatically.
- 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
- Validate the URL scheme in the frontend before save.
- Never accept user-supplied file:// or data:// inputs.
- Treat this error as a possible LFI attempt and audit the source.
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
- This Chromium executable path is not allowed by default. If
- Chromium not found, please specify Chromium executable path
- ${e.message}
- ${res.status()}
- Invalid service name. Only alphanumeric characters and '.',
AI-assisted analysis of louislam/uptime-kuma@6b5ea01557 (2026-08-12).
Data as JSON: /api/errors/4a2675bd4e3c12c2.
Report an issue: GitHub.