{"record":{"id":"6ede1c261c997a46","repo":"docusealco/docuseal","slug":"error-loading-uri-only-https-is-allowed","errorCode":null,"errorMessage":"Error loading: #{uri}. Only HTTPS is allowed.","messagePattern":"Error loading: #(.+?)\\. Only HTTPS is allowed\\.","errorType":"exception","errorClass":"DownloadUtils::UnableToDownload","httpStatus":422,"severity":"error","filePath":"lib/download_utils.rb","lineNumber":55,"sourceCode":"\n  def call(url, validate: Docuseal.multitenant?)\n    uri = begin\n      URI(url)\n    rescue URI::Error\n      Addressable::URI.parse(url).normalize\n    end\n\n    validate_uri!(uri) if validate\n\n    resp = conn(validate:).get(uri)\n\n    raise UnableToDownload, \"Error loading: #{uri}\" if resp.status >= 400\n\n    resp\n  end\n\n  def validate_uri!(uri)\n    raise UnableToDownload, \"Error loading: #{uri}. Only HTTPS is allowed.\" if uri.scheme != 'https' ||\n                                                                               [443, nil].exclude?(uri.port)\n    raise UnableToDownload, \"Error loading: #{uri}. Can't download from localhost.\" if uri.host.in?(LOCALHOSTS)\n  end\n\n  def conn(validate: Docuseal.multitenant?)\n    Faraday.new do |faraday|\n      faraday.response :follow_redirects, callback: lambda { |_, new_env|\n        validate_uri!(new_env[:url]) if validate\n      }\n    end\n  end\nend\n","sourceCodeStart":37,"sourceCodeEnd":68,"githubUrl":"https://github.com/docusealco/docuseal/blob/004a22c1c88109c7ba0b567df011a8cb13894001/lib/download_utils.rb#L37-L68","documentation":"validate_uri! in DownloadUtils rejects any download URL that is not https on port 443 (or default port), appending '. Only HTTPS is allowed.' to the message. The check runs up front and again on every redirect hop (the follow_redirects callback re-validates), and only when validation is active - call and conn default validate: to Docuseal.multitenant?, so it is enforced on multitenant installs and skipped on plain self-hosted ones. An https URL that redirects to http:// also raises this.","triggerScenarios":"Passing http://example.com/file.pdf; https on a custom port such as 8443; an https URL whose redirect chain drops to http; code tested on self-hosted (validation off) then run in multitenant mode.","commonSituations":"Internal tooling pointing at plain-HTTP intranet file sources; test environments without TLS; redirect chains through http intermediates; assuming self-hosted behavior is universal.","solutions":["Serve the file over plain HTTPS on port 443 (https://host/file, no port suffix) and use that URL.","Fix redirect chains so every hop stays https.","If you genuinely need internal http sources on a self-hosted install, understand validation is skipped only because Docuseal.multitenant? is false - do not flip that flag to work around it.","For local files, upload directly instead of downloading by URL."],"exampleFix":"# before\nattach_url: 'http://files.example.com/doc.pdf'\n\n# after\nattach_url: 'https://files.example.com/doc.pdf'","handlingStrategy":"validation","validationCode":"uri = URI(url)\nraise UnableToDownload, 'https required' unless uri.scheme == 'https' && [443, nil].include?(uri.port)","typeGuard":null,"tryCatchPattern":"rescue UnableToDownload => e\n  show_user_friendly_message(e.message) # includes the offending URI","preventionTips":["Normalize all user-supplied URLs to https before storing","Test redirect chains, not just the initial URL","Document that enforcement is multitenant-only for self-hosters"],"tags":["ssrf","https","download","url-validation","security"],"backgroundTag":"url-scheme-validation","analyzedSha":"004a22c1c88109c7ba0b567df011a8cb13894001","analyzedAt":"2026-08-21T13:38:23.343Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}