github/spec-kit · error · ExtensionError
URL must use HTTPS (HTTP is only allowed for localhost)
Error message
URL must use HTTPS (HTTP is only allowed for localhost)
What it means
The URL installer's first gate: is_https_or_localhost_http(url) returned false, meaning the scheme is not HTTPS (and not http:// on a loopback host) — or the URL failed basic parse/shape checks. This is a deliberate security control: extension archives are executable content, so plaintext HTTP is only tolerated for localhost development.
Source
Thrown at src/specify_cli/extensions/_commands.py:123
force: bool = False,
):
"""Download an archive from *url* and install it, reusing the hardened path.
Shares the same download hardening as ``extension add --from``:
HTTPS enforcement, the catalog's authenticated + redirect-guarded
``_open_url`` fetch, a bounded (50 MiB) response read, archive-format
detection (ZIP or tar.gz/tgz), and a TOCTOU-safe transient download file
consumed directly by ``install_from_zip``.
Returns the installed manifest. Raises ``ExtensionError`` on any failure so
callers can present a uniform message without a second downloader.
"""
import urllib.error
from . import ExtensionCatalog, ExtensionError
if not is_https_or_localhost_http(url):
raise ExtensionError(
"URL must use HTTPS (HTTP is only allowed for localhost)"
)
download_dir = _validate_safe_cache_dir(project_root)
archive_filename = f"extension-url-download-{uuid4().hex}.archive"
# Only used for diagnostic messages: the real archive is a transient inode
# (unlinked on POSIX, O_TEMPORARY on Windows) consumed via ``archive_file``
# below, so this path is never opened again.
archive_path = download_dir / archive_filename
try:
dl_catalog = ExtensionCatalog(project_root)
download_url = url
extra_headers = None
resolved_url = dl_catalog._resolve_github_release_asset_api_url(download_url)
if resolved_url:
download_url = resolved_url
extra_headers = {"Accept": "application/octet-stream"}View on GitHub (pinned to bf88c9f9a8)
Solutions
- Serve the archive over HTTPS (e.g. behind a TLS-terminating proxy or with a self-signed cert added to the trust store) and use the https:// URL
- For local development, keep HTTP but address it as localhost/127.0.0.1 (::1) so the loopback exemption applies
- For local files, skip the URL path entirely — install from the local archive path or directory
- Fix the typo'd scheme (http:// -> https://)
Example fix
# before specify extension install http://internal-mirror:8080/my-ext.zip # after (localhost exemption) python -m http.server 8080 & specify extension install http://localhost:8080/my-ext.zip # or specify extension install https://mirror.example.com/my-ext.zip
Defensive patterns
Strategy: validation
Validate before calling
from urllib.parse import urlparse
def url_allowed_for_install(url: str) -> bool:
p = urlparse(url)
host = (p.hostname or '').lower()
return p.scheme == 'https' or (
p.scheme == 'http' and host in ('localhost', '127.0.0.1', '::1')
)
assert url_allowed_for_install('https://example.com/ext.zip') Type guard
def assert_installable_url(url: str) -> None:
if not url_allowed_for_install(url):
raise ValueError(
'Extension URLs must be https:// (http:// only on localhost)'
) Prevention
- Use https:// for all remote extension sources
- For local files, pass the file path instead of a file:// URL
- Use localhost (not 0.0.0.0/LAN IP) for plain-http dev mirrors
When it happens
Trigger: Calling the install-from-URL path (`specify extension install <url>` or _commands installer) with http:// on a non-localhost host, or with a scheme like file:// or ftp://.
Common situations: Using http:// to dodge TLS errors on an internal mirror; copy-pasting a file:// path instead of a URL; dev server on 0.0.0.0 or a LAN IP over plain http; typo dropping the s in https.
Related errors
- Catalog url must use HTTPS (got {parsed.scheme}://). HTTP is
- Catalog entry '{resolved.entry.id}' has a non-HTTP(S) downlo
- Refusing to download {label} over non-HTTPS URL: {url}
- {exc}
- Extension '{ext_spec}' is in the '{catalog_name}' catalog bu
AI-assisted analysis of github/spec-kit@bf88c9f9a8 (2026-08-14).
Data as JSON: /api/errors/e3b91b6de184d7a3.
Report an issue: GitHub.