#1515·reqwest

IP Limiting

Author: kevincoxCreated Apr 1, 2022Updated Jun 29, 2026

I am building a service that needs to fetch user-provided URLs. I have added some protection such that the user can't request localhost, whatever.internal or 10.0.0.1. However I have not found a way to block a request such as internal.some-real-domain.example which resolves to an internal IP address. I think it would be valuable if reqwest could provide an internal hook, much like the redirect hook that could validate (and maybe rewrite) IP addresses before connections are made.

The following cases should be handled:

  1. Top level requests to IP addresses.
  2. Top level requests to domains.
  3. Redirects to either of the above.

A possible API would be something like this:

rust
impl ClientBuilder {
  pub fn ip(self, policy: reqwest::ip::Policy) -> Self;
}

mod ip {
  struct Policy;

  impl Policy {
    pub fn custom(fn: impl Fn(Attempt<'_>) -> Action + Send + Sync + 'static) -> Self;
  }

  struct Attempt;

  impl Attempt {
    pub fn url(&self) -> &Url;
    pub fn ip(&self) -> std::net::SocketAddr;

    pub fn allow(self) -> Action;
    pub fn connect_to(self, std::net::SocketAddr) -> Action;
    pub fn error(self, error: impl Into<Box<dyn StdError + Send + Sync>>) -> Action;
  }

  struct Action;
}

(This is modeled after the redirect module but maybe the naming could be tweaked to be more clear in this case.)

For my use I would do something like this:

rust
reqwest::Client::build()
  .ip(request::ip::Policy::custom(|attempt| {
    if attempt.ip().is_global() {
      attempt.allow()
    } else {
      attempt.error(FeedError::Host(attempt.ip().to_string())
    }
  })

Unanswered questions:

  • Async?
  • How to handle non-IP connections. For example UNIX sockets or Tor addresses. I think maybe the IP policy can be skipped for these as they are both knowable based on the URL (a hostname can't resolve to either of these AFAIK).
  • This always resolves the IP. Maybe it could be made lazy to make this essentially a full DNS resolution hook, but then we are back to "async?"

Alternatives:

  • The user can manually resolve the hostname then pass the IP to reqwest along with Host: header and SSL verification configuration. This is difficult and error prone.
  • A general DNS resolution hook can be added. This may be a bit more flexible but would make it harder to support things like SRV records (or whatever alternative is currently being proposed).
  • Do nothing. Then users of reqwest are at risk of information leaks if they connect to user-provided URLs.