docusealco/docuseal · error · DownloadUtils::UnableToDownload
Error loading: #{uri}. Can't download from localhost.
Error message
Error loading: #{uri}. Can't download from localhost. What it means
The same validate_uri! rejects downloads whose host is in LOCALHOSTS - an explicit set covering IPv4/IPv6 loopback forms (localhost, 127.0.0.1, ::1, 0.0.0.0, ip6-loopback and variants) - appending '. Can't download from localhost.'. This is SSRF protection: it stops multitenant workloads from fetching files off the server's own loopback interfaces. It also re-checks every redirect hop, so an external URL redirecting to 127.0.0.1 raises too; the same constant backs webhook egress protection (SendWebhookRequest::LocalhostError).
Source
Thrown at lib/download_utils.rb:57
uri = begin
URI(url)
rescue URI::Error
Addressable::URI.parse(url).normalize
end
validate_uri!(uri) if validate
resp = conn(validate:).get(uri)
raise UnableToDownload, "Error loading: #{uri}" if resp.status >= 400
resp
end
def validate_uri!(uri)
raise UnableToDownload, "Error loading: #{uri}. Only HTTPS is allowed." if uri.scheme != 'https' ||
[443, nil].exclude?(uri.port)
raise UnableToDownload, "Error loading: #{uri}. Can't download from localhost." if uri.host.in?(LOCALHOSTS)
end
def conn(validate: Docuseal.multitenant?)
Faraday.new do |faraday|
faraday.response :follow_redirects, callback: lambda { |_, new_env|
validate_uri!(new_env[:url]) if validate
}
end
end
end
View on GitHub (pinned to 004a22c1c8)
Solutions
- Host the file on a public HTTPS host and use that URL.
- For a legitimate internal integration on self-hosted, run in non-multitenant mode where validation is skipped - and accept the risk consciously.
- Expose the internal service through an authenticated public endpoint instead of loopback.
- If you must allow a specific internal host, audit the LOCALHOSTS set and understand why the host is listed before touching it.
Example fix
# before attach_url: 'http://localhost:3000/files/doc.pdf' # after attach_url: 'https://public-host.example.com/files/doc.pdf'
Defensive patterns
Strategy: validation
Validate before calling
host = URI(url).host raise UnableToDownload, 'localhost blocked' if DownloadUtils::LOCALHOSTS.include?(host)
Try / catch
rescue UnableToDownload => e # do not retry: the SSRF guard is a terminal rejection by design report_invalid_source_url(e.message)
Prevention
- Never accept loopback or internal URLs from user input
- Validate redirect targets, not just the initial URL
- Resolve untrusted hostnames and check for loopback answers
When it happens
Trigger: Passing http(s)://localhost/... or 127.0.0.1 URLs as file sources; hostnames resolving to loopback; redirect chains ending on a loopback host; testing integrations against a local dev server from a validating instance.
Common situations: Developers testing URL-download features against local fixtures; accidental attempts to pull internal services or cloud metadata; shared dev environments with loopback aliases.
Related errors
- Error loading: #{uri}. Only HTTPS is allowed.
- File type '.#{extension}' is not allowed.
- Error loading: #{uri}
- File type '.#{extension}' is not allowed.
- File type '.#{detected_extensions.first}' is not allowed.
AI-assisted analysis of docusealco/docuseal@004a22c1c8 (2026-08-21).
Data as JSON: /api/errors/a6ea9800aa29383b.
Report an issue: GitHub.