DIYgod/RSSHub · warning
Invalid URL property: ${prop}
Error message
Invalid URL property: ${prop} What it means
Thrown by validateTemplate in the anti-hotlink middleware when a user-supplied hotlink template contains a ${prop} or ${prop_ue} placeholder whose `prop` is not a standard URL object property. The allowed set is exactly: hash, host, hostname, href, origin, password, pathname, port, protocol, search, searchParams, username. The `_ue` suffix requests URL-encoding of the value.
Source
Thrown at lib/middleware/anti-hotlink.ts:97
replaceUrls($, '*[data-rsshub-image="href"]', image_hotlink_template, 'href');
}
if (multimedia_hotlink_template) {
replaceUrls($, 'video, video > source, audio, audio > source', multimedia_hotlink_template);
if (!image_hotlink_template) {
replaceUrls($, 'video[poster]', multimedia_hotlink_template, 'poster');
}
}
return $.html();
};
const validateTemplate = (template?: string) => {
if (!template) {
return;
}
for (const match of template.matchAll(templateRegex)) {
const prop = match[1].endsWith('_ue') ? match[1].slice(0, -3) : match[1];
if (!allowedUrlProperties.has(prop)) {
throw new Error(`Invalid URL property: ${prop}`);
}
}
};
const middleware: MiddlewareHandler = async (ctx, next) => {
await next();
let imageHotlinkTemplate: string | undefined;
let multimediaHotlinkTemplate: string | undefined;
// Read params if enabled
if (config.feature.allow_user_hotlink_template) {
// By default, the config turns these features off. Set corresponding config to
// true to turn this feature on.
// A risk is that the media URLs will be replaced by user-supplied templates,
// so a user could literally take the control of "where are the media from",
// but only in their personal-use feed URL.
multimediaHotlinkTemplate = ctx.req.query('multimedia_hotlink_template');View on GitHub (pinned to bed535e087)
Solutions
- Replace the invalid placeholder with one of: ${hash} ${host} ${hostname} ${href} ${origin} ${password} ${pathname} ${port} ${protocol} ${search} ${searchParams} ${username}.
- Append `_ue` (e.g. ${pathname_ue}) only for URL-encoding, never inside the property name otherwise.
- If you do not need a custom template, omit the query param so the config default is used.
- Disable allow_user_hotlink_template if templates are not required.
Example fix
// before
?image_hotlink_template=https://proxy.example.com/?url=${host_name}${pathname}
// after
?image_hotlink_template=https://proxy.example.com/?url=${host}${pathname_ue} Defensive patterns
Strategy: validation
Validate before calling
const ALLOWED = new Set(['hash','host','hostname','href','origin','password','pathname','port','protocol','search','searchParams','username']);
const validateTemplate = (tpl: string) => {
for (const m of tpl.matchAll(/\$\{([^{}]+)\}/g)) {
const prop = m[1].endsWith('_ue') ? m[1].slice(0, -3) : m[1];
if (!ALLOWED.has(prop)) throw new Error(`Invalid URL property: ${prop}`);
}
};
validateTemplate(imageHotlinkTemplate); Type guard
const isPlaceholderValid = (p: string): boolean =>
ALLOWED.has(p.endsWith('_ue') ? p.slice(0, -3) : p); Try / catch
try { validateTemplate(tpl); } catch (e) {
// fall back to the default config template rather than failing the request
tpl = undefined;
} Prevention
- Restrict allow_user_hotlink_template to trusted users only.
- Provide users a documented allow-list of placeholders.
- Prefer ${pathname_ue} over hand-rolled encoding.
When it happens
Trigger: config.feature.allow_user_hotlink_template is true and the request (or config) supplies an image_hotlink_template / multimedia_hotlink_template containing a placeholder like ${foo} or ${host_name} that is not in the allowed set.
Common situations: Typo in the template placeholder; assuming a custom field (e.g. ${domain}) exists on the URL object; copying a template from docs that used a property name that was never supported.
Related errors
- Invalid Engine Value: ${engine}, please check your config.
- Authentication failed. Access denied.\n${requestPath}
- Invalid parameter brief. Please check the doc https://docs.r
- Bad type
- Invalid lang
AI-assisted analysis of DIYgod/RSSHub@bed535e087 (2026-08-12).
Data as JSON: /api/errors/8468be7f34486b5d.
Report an issue: GitHub.