Skip to content

[Bug]: HTTP crawler puts proxy credentials into the URL unencoded #2326

Closed

Description

@alkaz-nodemaven

crawl4ai version

0.9.4 (same code on develop at 1f68e5b)

Expected Behavior

AsyncHTTPCrawlerStrategy works through a proxy whose username or password contains #, /, ? or %, as the browser path already does (it passes username and password to Playwright as separate fields).

Current Behavior

_format_proxy_url (async_crawler_strategy.py:2634) builds f"{protocol}://{username}:{password}@{rest}" without percent-encoding. Results with aiohttp 3.14.3 against a local proxy:

password result
plainpass, p@ss correct credentials sent
p#ss, pa/ss, p?ss aiohttp.InvalidURL, request never sent
p%41ss proxy receives pAss

The InvalidURL message is the whole URL, and the except aiohttp.ClientError branch re-raises it as HTTPCrawlerError(f"HTTP client error: {str(e)}"), so the password ends up in the error text.

Is this reproducible?

Yes

Inputs Causing the Bug

ProxyConfig(server="http://host:port", username="user", password="p#ss") with AsyncHTTPCrawlerStrategy.

Steps to Reproduce

Pass the output of _format_proxy_url as proxy= to aiohttp.ClientSession.get, which is what the crawler does at async_crawler_strategy.py:2737.

Code snippets

from urllib.parse import quote

return f"{protocol}://{quote(username, safe='')}:{quote(password, safe='')}@{rest}"

(both branches). Happy to send it as a PR to develop with a test.

OS

Windows 10

Python version

3.11

Activity

  1. omarbounawarapy commented on Oct 1, 2026

    @omarbounawarapy
    Contributor

    I'd like to take this. I reproduced it against a local proxy: passwords with #, / or ? raise InvalidURL, and % gets decoded before it reaches the proxy. The fix is to percent-encode the username and password in _format_proxy_url. Should the password showing up in the HTTPCrawlerError text be fixed in the same PR, or separately?

  2. added
    🐞 BugSomething isn't working
    and removed on Oct 12, 2026
  3. ntohidi commented on Oct 12, 2026

    @ntohidi
    SponsorCollaborator

    Thanks for the clear report. Confirmed on develop. Here is the root cause.

    Root cause

    _format_proxy_url puts the username and password into the proxy URL as they are (async_crawler_strategy.py#L2643-L2649):

    return f"{protocol}://{username}:{password}@{rest}"

    So #, / and ? end the user-info part of the URL early (aiohttp raises InvalidURL), and % is read as an escape code (the proxy gets the wrong password).

    The password also reaches the error text: InvalidURL's message is the full URL, and the except aiohttp.ClientError branch wraps it as HTTPCrawlerError(f"HTTP client error: {str(e)}") (L2816-L2818). With password="s3cret#pw" the error reads http://user:s3cret#pw@127.0.0.1:9.

    Fix direction

    Percent-encode the username and password with quote(..., safe='') before building the URL. With that change the URL is valid, and the error above becomes a plain connection error with no password in it. #2327 does this and is under review.

    Hiding credentials in the wrapped error text is a separate safety step and can go in its own PR.

  4. alkaz-nodemaven commented on Oct 12, 2026

    @alkaz-nodemaven
    ContributorAuthor

    Thanks for confirming. I'll send the separate PR for the error text: mask proxy credentials in the messages _handle_http wraps into HTTPCrawlerError and the connection/timeout errors, so a password doesn't reach logs through any error type, not only InvalidURL. It won't touch _format_proxy_url, so it doesn't overlap #2327.

  5. added
    ⚙ DoneBug fix, enhancement, FR that's completed pending release
    and removed
    ⚙️ In-progressIssues, Features requests that are in Progress
    on Oct 12, 2026
  6. ntohidi commented on Oct 14, 2026

    @ntohidi
    SponsorCollaborator

    Fixed by #2327 and #2361, released in v0.9.5. Upgrade with pip install -U crawl4ai. Release notes: https://github.com/unclecode/crawl4ai/blob/main/docs/blog/release-v0.9.5.md

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    ⚙ DoneBug fix, enhancement, FR that's completed pending release🐞 BugSomething isn't working🥸 - MediumPriority - Medium

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions