DIYgod/RSSHub · error · ConfigNotFoundError

This RSS is disabled unless 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN

Error message

This RSS is disabled unless 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN' is set to 'true'.

What it means

Thrown by the lemmy route handler as ConfigNotFoundError when the instance hostname extracted from the community param is not in the allowedDomain list ({lemmy.world, lemm.ee, lemmy.ml, sh.itjust.works, feddit.de, hexbear.net, beehaw.org, lemmynsfw.com, lemmy.ca, programming.dev}) and config.feature.allow_user_supply_unsafe_domain is falsy. This is the same SSRF-guard pattern as javdb: the route issues API calls to a user-influenced host, so only trusted instances are reachable by default.

Source

Thrown at lib/routes/lemmy/index.ts:73

        supportPodcast: false,
        supportScihub: false,
    },
    name: 'Community',
    maintainers: ['wb14123', 'pseudoyu'],
    handler,
};

async function handler(ctx) {
    const sort = ctx.req.param('sort') ?? 'Active';
    const community = ctx.req.param('community');
    const communitySlices = community.split('@');
    if (communitySlices.length !== 2) {
        throw new InvalidParameterError(`Invalid community: ${community}`);
    }
    const instance = community.split('@', 2)[1];
    const allowedDomain = ['lemmy.world', 'lemm.ee', 'lemmy.ml', 'sh.itjust.works', 'feddit.de', 'hexbear.net', 'beehaw.org', 'lemmynsfw.com', 'lemmy.ca', 'programming.dev'];
    if (!config.feature.allow_user_supply_unsafe_domain && !allowedDomain.includes(new URL(`http://${instance}/`).hostname)) {
        throw new ConfigNotFoundError(`This RSS is disabled unless 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN' is set to 'true'.`);
    }

    const communityUrl = `https://${instance}/api/v3/community?name=${community}`;
    const communityData = await cache.tryGet(communityUrl, async () => {
        const result = await got({ method: 'get', url: communityUrl, headers: { 'Content-Type': 'application/json' } });
        return result.data.community_view.community;
    });

    const postUrl = `https://${instance}/api/v3/post/list?type_=All&sort=${sort}&community_name=${community}&limit=50`;
    const postData = await cache.tryGet(
        postUrl,
        async () => {
            const result = await got({ method: 'get', url: postUrl, headers: { 'Content-Type': 'application/json' } });
            return result.data;
        },
        config.cache.routeExpire,
        false
    );

View on GitHub (pinned to bed535e087)

Solutions

  1. Use one of the allow-listed instances (lemmy.world, lemmey.ml, programming.dev, etc.)
  2. For a trusted instance you run, add its hostname to allowedDomain in lib/routes/lemmy/index.ts:69
  3. Set ALLOW_USER_SUPPLY_UNSAFE_DOMAIN=true in config only if you accept the SSRF risk
  4. Note the instance check uses exact hostname match — subdomains (e.g. ml.lemmy.world) are rejected

Example fix

// before
const allowedDomain = ['lemmy.world', 'lemm.ee', 'lemmy.ml', 'sh.itjust.works', 'feddit.de', 'hexbear.net', 'beehaw.org', 'lemmynsfw.com', 'lemmy.ca', 'programming.dev'];
// after — add your trusted instance
const allowedDomain = ['lemmy.world', 'lemm.ee', 'lemmy.ml', 'sh.itjust.works', 'feddit.de', 'hexbear.net', 'beehaw.org', 'lemmynsfw.com', 'lemmy.ca', 'programming.dev', 'discuss.tchncs.de'];
Defensive patterns

Strategy: validation

Validate before calling

import config from '@/utils/config';
const ALLOWED = new Set(['lemmy.world', 'lemm.ee', 'lemmy.ml', 'sh.itjust.works', 'feddit.de', 'hexbear.net', 'beehaw.org', 'lemmynsfw.com', 'lemmy.ca', 'programming.dev']);
function assertAllowedInstance(instance: string): void {
  const host = new URL(`http://${instance}/`).hostname;
  if (!config.feature.allow_user_supply_unsafe_domain && !ALLOWED.has(host)) {
    throw new ConfigNotFoundError(`instance '${host}' not allowed`);
  }
}

Type guard

const ALLOWED = new Set(['lemmy.world', 'lemm.ee', 'lemmy.ml', 'sh.itjust.works', 'feddit.de', 'hexbear.net', 'beehaw.org', 'lemmynsfw.com', 'lemmy.ca', 'programming.dev']);
function isAllowedLemmyInstance(instance: string): boolean {
  return ALLOWED.has(new URL(`http://${instance}/`).hostname);
}

Try / catch

// validate before any API call
const { instance } = parseCommunity(community);
if (!isAllowedLemmyInstance(instance) && !config.feature.allow_user_supply_unsafe_domain) {
  return ctx.json({ error: 'instance not allowed', allowed: [...ALLOWED] }, 400);
}
try { return await handler(ctx); }
catch (e) { if (e instanceof ConfigNotFoundError) return ctx.json({ error: e.message }, 403); throw e; }

Prevention

When it happens

Trigger: Request like /lemmy/<name>@<untrusted-instance>/Hot where the instance is not in allowedDomain and the feature flag is off. The handler builds `new URL(http://<instance>/)` and checks hostname membership; failure throws.

Common situations: User points at a smaller/newer Lemmy instance not in the allow-list; self-hoster without ALLOW_USER_SUPPLY_UNSAFE_DOMAIN; attempt to use the route as a server-side request proxy to an internal host.

Related errors


AI-assisted analysis of DIYgod/RSSHub@bed535e087 (2026-08-12). Data as JSON: /api/errors/88bc93995dd189f9. Report an issue: GitHub.