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
- Map only the https scheme to the H2 handler and leave http on the default handler: DOWNLOAD_HANDLERS = {'https': '...http2.H2DownloadHandler'}.
- Or use the httpx handler with HTTPX_HTTP2_ENABLED = True, which handles both http and https.
- 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
- Map the H2 handler to the https scheme only.
- If the site mixes schemes, use the httpx handler for both instead.
- Test redirects: an http downgrade from https will cross handler boundaries.
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
- Unsupported URL scheme '{scheme}': {self._notconfigured[sche
- HTTP/2 support in {type(self).__name__} requires the 'httpx2
- {type(self).__name__} requires a Twisted reactor.
- Invalid entry point {entry_point.name}
- {type(self).__name__} requires the asyncio support. Make sur
AI-assisted analysis of scrapy/scrapy@06af687662 (2026-08-15).
Data as JSON: /api/errors/ec61c01599931284.
Report an issue: GitHub.