scrapy/scrapy · error · UnsupportedURLSchemeError

{type(self).__name__} doesn't support plain HTTP.

Error message

{type(self).__name__} doesn't support plain HTTP.

What it means

Raised as UnsupportedURLSchemeError when H2DownloadHandler.download_request receives a plain http:// URL. The handler only implements h2 over TLS; it is meant to be mapped to the https scheme only, and hitting it with http means DOWNLOAD_HANDLERS was configured to route http traffic to it.

Source

Thrown at scrapy/core/downloader/handlers/http2.py:49

class H2DownloadHandler(BaseDownloadHandler):
    lazy = True

    def __init__(self, crawler: Crawler):
        if not crawler.settings.getbool("TWISTED_REACTOR_ENABLED"):
            raise NotConfigured(f"{type(self).__name__} requires a Twisted reactor.")
        super().__init__(crawler)
        self._crawler = crawler

        from twisted.internet import reactor

        self._pool = H2ConnectionPool(reactor, crawler)
        self._context_factory = _load_context_factory_from_settings(crawler)
        self._bind_address = crawler.settings.get("DOWNLOAD_BIND_ADDRESS")

    async def download_request(self, request: Request) -> Response:
        if urlparse_cached(request).scheme == "http":  # pragma: no cover
            raise UnsupportedURLSchemeError(
                f"{type(self).__name__} doesn't support plain HTTP."
            )
        agent = _ScrapyH2Agent(
            context_factory=self._context_factory,
            pool=self._pool,
            bind_address=self._bind_address,
            crawler=self._crawler,
        )
        assert self._crawler.spider
        with wrap_twisted_exceptions():
            return await maybe_deferred_to_future(
                agent.download_request(request, self._crawler.spider)
            )

    async def close(self) -> None:
        self._pool.close_connections()

View on GitHub (pinned to 06af687662)

Solutions

  1. Map only the https scheme to the H2 handler and leave http on the default handler: DOWNLOAD_HANDLERS = {'https': '...http2.H2DownloadHandler'}.
  2. Or use the httpx handler with HTTPX_HTTP2_ENABLED = True, which handles both http and https.
  3. Ensure yielded/redirected URLs use https when the H2 handler owns their scheme.

Example fix

# before
DOWNLOAD_HANDLERS = {
    'http': 'scrapy.core.downloader.handlers.http2.H2DownloadHandler',
    'https': 'scrapy.core.downloader.handlers.http2.H2DownloadHandler',
}

# after
DOWNLOAD_HANDLERS = {'https': 'scrapy.core.downloader.handlers.http2.H2DownloadHandler'}
Defensive patterns

Strategy: validation

Validate before calling

# settings.py — never map http:// to the h2 handler
assert 'http' not in {
    k for k, v in DOWNLOAD_HANDLERS.items()
    if 'handlers.http2' in v
}, 'H2DownloadHandler only supports https targets'

Prevention

When it happens

Trigger: DOWNLOAD_HANDLERS['http'] (or a wildcard mapping) pointing at H2DownloadHandler while the spider yields http:// requests; the handler checks urlparse_cached(request).scheme == 'http' and rejects.

Common situations: Copying a DOWNLOAD_HANDLERS snippet that maps both http and https to the H2 handler; redirect chains that downgrade https to http while only the https handler was replaced (less common); misreading the handler as a general HTTP/2-for-everything handler.

Related errors


AI-assisted analysis of scrapy/scrapy@06af687662 (2026-08-15). Data as JSON: /api/errors/ec61c01599931284. Report an issue: GitHub.