nocodb/nocodb · error · SsrfBlockedHostError
Connection to internal hosts is not allowed
Error message
Connection to internal hosts is not allowed
What it means
Thrown by assertExternalDbHostAllowed when the external-database SSRF guard sees a hostname that is literally a non-routable sentinel: '0.0.0.0', '::', or any case variant of 'localhost'. This is the literal-hostname branch — it fires before any DNS lookup, so even a hosts-file remap will not bypass it. The guard is enabled unless NC_DISABLE_SSRF_PROTECTION=true, NC_ALLOW_LOCAL_EXTERNAL_DBS=true (self-hosted only), or the backend forced it off; on cloud, forceEnforce is always on.
Source
Thrown at packages/noco-integrations/core/src/utils/externalDbSsrf.ts:87
}
/**
* Throws `SsrfBlockedHostError` if `host` resolves to a non-routable range.
* No-op when SSRF protection is disabled (see `isDbSsrfProtectionEnabled`).
*/
export async function assertExternalDbHostAllowed(
host: unknown,
): Promise<void> {
if (!isDbSsrfProtectionEnabled()) return;
if (typeof host !== 'string' || host.length === 0) return;
const trimmed = host.trim();
if (
trimmed === '0.0.0.0' ||
trimmed === '::' ||
/^localhost$/i.test(trimmed)
) {
throw new SsrfBlockedHostError();
}
// TOCTOU note: the driver re-resolves at connect-time; a controlled DNS
// record with short TTL could flip between this lookup and the driver's
// connect(). Mitigating fully requires passing the resolved IP to the
// driver, which is per-driver wiring out of scope here.
let resolvedIps: string[] = [];
if (isIP(trimmed)) {
resolvedIps = [trimmed];
} else {
try {
const records = await dns.lookup(trimmed, { all: true });
resolvedIps = records.map((r) => r.address);
} catch {
// Let the driver surface DNS failures.
return;
}
}View on GitHub (pinned to d3caaf4e89)
Solutions
- Point the integration at the actual reachable address (the database's private IP, a DNS name that resolves to it, or the loopback IP if you intentionally mean localhost AND you enable the bypass).
- On self-hosted only: set NC_ALLOW_LOCAL_EXTERNAL_DBS=true (preferred, scoped to external DBs) or NC_DISABLE_SSRF_PROTECTION=true (broader) and restart the server.
- If the DB lives on another machine, use its hostname or non-loopback IP — do not use 'localhost' as a synonym for 'the DB server'.
- On cloud this guard cannot be disabled; move the database to a routable address reachable from the cloud network.
Example fix
// before
new MssqlIntegration({ host: 'localhost', port: 1433, ... });
// -> SsrfBlockedHostError: Connection to internal hosts is not allowed
// after (self-hosted, intentional)
// .env: NC_ALLOW_LOCAL_EXTERNAL_DBS=true
new MssqlIntegration({ host: 'localhost', port: 1433, ... }); Defensive patterns
Strategy: validation
Validate before calling
import { isIP } from 'net';
function isLiteralInternalHost(host: string): boolean {
const t = host.trim().toLowerCase();
return t === 'localhost' || t === '0.0.0.0' || t === '::';
}
// before opening the connection
if (isLiteralInternalHost(host) && process.env.NC_ALLOW_LOCAL_EXTERNAL_DBS !== 'true') {
throw new Error(`Refusing localhost/any-address host '${host}' under SSRF protection`);
} Type guard
function isNonRoutableLiteral(host: unknown): host is string {
return (
typeof host === 'string' &&
['localhost', '0.0.0.0', '::'].includes(host.trim().toLowerCase())
);
} Try / catch
import { SsrfBlockedHostError } from '@noco-integrations/core/utils/externalDbSsrf';
try {
await assertExternalDbHostAllowed(host);
await openConnection(host);
} catch (err) {
if (err instanceof SsrfBlockedHostError) {
ui.warn('Localhost hosts are blocked by SSRF protection; ask your admin to enable NC_ALLOW_LOCAL_EXTERNAL_DBS or use a routable host.');
return;
}
throw err;
} Prevention
- Always configure external DBs with their routable DNS name, not 'localhost'.
- For dev, set NC_ALLOW_LOCAL_EXTERNAL_DBS=true in .env and remember to remove it before deploying to cloud.
- Validate the host in the UI before submitting the connection-test request.
When it happens
Trigger: An integration tries to open an external MSSQL/Postgres/MySQL/etc. connection where the configured host string equals 'localhost', '0.0.0.0', or '::' (IPv6 any). Also triggered by UI 'Test Connection' or workflow node setup that submits such a host while SSRF protection is enabled.
Common situations: Developer running NocoDB + a local database on the same host and pointing an integration at 'localhost'; a config export that used 0.0.0.0 as a wildcard bind address mistakenly reused as the connect target; an IPv6-only dev box with '::' as the host.
Related errors
- Invalid database configuration. Please verify your database
- Integrations table not found
- Integration not configured properly
- Refresh token not available for this integration
- Integration loader not available. This node must be executed
AI-assisted analysis of nocodb/nocodb@d3caaf4e89 (2026-08-12).
Data as JSON: /api/errors/ef7f08d7d41e445f.
Report an issue: GitHub.