HTTP-restricted networks that route outbound traffic through proxies often treat tunneled connections differently from plain proxy requests. Forcing cURL to use proxy CONNECT tunneling aligns command‑line behavior with browser traffic, simplifies reproduction of proxy issues, and helps validate how egress controls handle specific destinations or ports. Consistent tunneling behavior is especially useful when debugging authentication problems, deep packet inspection policies, or SSL offloading quirks.

With an HTTP proxy configured, cURL normally sends absolute-form HTTP requests for http:// URLs and reserves the CONNECT method for https:// and other tunneling scenarios. The –proxy option and environment variables such as HTTP_PROXY determine which proxy receives the traffic, while flags like --proxytunnel instruct cURL to negotiate a CONNECT tunnel to the destination host and port before any application data flows. Once the proxy replies with a successful CONNECT status, subsequent bytes travel inside the tunnel as if the client were talking directly to the origin server.

Some corporate proxies restrict tunneling to approved ports or hosts and may log or block unauthorized CONNECT attempts. The examples below assume a working HTTP proxy hostname and port, a recent cURL build with proxy support compiled in, and a terminal on a Unix‑like system; the same flags behave similarly on Windows. Excessive failed CONNECT requests can trigger rate limits or security alerts, so careful testing and coordination with network administrators reduces risk.

Steps to force proxy CONNECT tunneling with cURL:

  1. Open a terminal where cURL is available.
    $ whoami
    root
  2. Check the installed cURL version to confirm support for proxy options.
    $ curl --version
    curl 8.5.0 (aarch64-unknown-linux-gnu) libcurl/8.5.0 OpenSSL/3.0.13 zlib/1.3 brotli/1.1.0 zstd/1.5.5 libidn2/2.3.7 libpsl/0.21.2 (+libidn2/2.3.7) libssh/0.10.6/openssl/zlib nghttp2/1.59.0 librtmp/2.3 OpenLDAP/2.6.7
    Release-Date: 2023-12-06, security patched: 8.5.0-2ubuntu10.6
    Protocols: dict file ftp ftps gopher gophers http https imap imaps ldap ldaps mqtt pop3 pop3s rtmp rtsp scp sftp smb smbs smtp smtps telnet tftp
    Features: alt-svc AsynchDNS brotli GSS-API HSTS HTTP2 HTTPS-proxy IDN IPv6 Kerberos Largefile libz NTLM PSL SPNEGO SSL threadsafe TLS-SRP UnixSockets zstd

    Any reasonably recent cURL release includes --proxy and --proxytunnel; exact versions and linked libraries vary by platform.

  3. Send an HTTP request through the proxy without tunneling to observe the default proxy behavior for a plain http:// URL.
    $ curl --verbose --proxy http://proxy.example.net:3128 --proxy-user user:password http://api.example.net/ -o /dev/null
    * Host proxy.example.net:3128 was resolved.
    * IPv6: (none)
    * IPv4: 127.0.0.1
    *   Trying 127.0.0.1:3128...
    * Connected to proxy.example.net (127.0.0.1) port 3128
    * Proxy auth using Basic with user 'user'
    > GET http://api.example.net/ HTTP/1.1
    > Host: api.example.net
    > Proxy-Authorization: Basic dXNlcjpwYXNzd29yZA==
    > User-Agent: curl/8.5.0
    > Accept: */*
    < HTTP/1.1 200 OK
    ##### snipped #####

    The absence of a CONNECT request shows that the proxy is handling the HTTP request directly instead of creating a tunnel to the origin server.

  4. Force use of the HTTP CONNECT method by repeating the request with --proxytunnel so the proxy establishes a tunnel to the target host and port.
    $ curl --verbose --proxy http://proxy.example.net:3128 --proxy-user user:password --proxytunnel http://api.example.net/ -o /dev/null
    * Host proxy.example.net:3128 was resolved.
    * IPv6: (none)
    * IPv4: 127.0.0.1
    *   Trying 127.0.0.1:3128...
    * Connected to proxy.example.net (127.0.0.1) port 3128
    * CONNECT tunnel: HTTP/1.1 negotiated
    * Proxy auth using Basic with user 'user'
    * Establish HTTP proxy tunnel to api.example.net:80
    > CONNECT api.example.net:80 HTTP/1.1
    > Host: api.example.net:80
    > Proxy-Authorization: Basic dXNlcjpwYXNzd29yZA==
    > User-Agent: curl/8.5.0
    > Proxy-Connection: Keep-Alive
    < HTTP/1.1 200 Connection established
    ##### snipped #####

    Some proxies block CONNECT entirely or restrict it to specific ports (often 443); repeated failed CONNECT attempts can trigger intrusion‑detection rules or temporary client bans.

  5. Include proxy authentication options when the proxy requires credentials before allowing tunnel creation.
    $ curl --verbose \
      --proxy http://proxy.example.net:3128 \
      --proxy-user user:password \
      --proxytunnel http://api.example.net/ -o /dev/null
    * Proxy auth using Basic with user 'user'
    > CONNECT api.example.net:80 HTTP/1.1
    > Host: api.example.net:80
    > Proxy-Authorization: Basic dXNlcjpwYXNzd29yZA==
    < HTTP/1.1 200 Connection established
    ##### snipped #####

    Replace the placeholder credentials with real proxy details or use --proxy-header for custom tokens when the proxy expects non‑Basic authentication schemes.

  6. Configure proxy settings via environment variables when multiple commands need the same HTTP proxy while still enabling CONNECT tunneling with --proxytunnel.
    $ export http_proxy=http://user:password@proxy.example.net:3128
    $ curl --verbose --proxytunnel http://api.example.net/ -o /dev/null
    * Host proxy.example.net:3128 was resolved.
    * IPv6: (none)
    * IPv4: 127.0.0.1
    *   Trying 127.0.0.1:3128...
    * Connected to proxy.example.net (127.0.0.1) port 3128
    * CONNECT tunnel: HTTP/1.1 negotiated
    ##### snipped #####

    Environment variables such as http_proxy and https_proxy keep command lines shorter while preserving control over tunneling behavior with --proxytunnel.

  7. Confirm that proxy CONNECT tunneling is active by searching the verbose output for the CONNECT request and a successful Connection established response from the proxy.
    $ curl --verbose --proxy http://proxy.example.net:3128 --proxy-user user:password --proxytunnel http://api.example.net/ -o /dev/null 2>&1 \
      | grep -E 'CONNECT|Connection established'
    * CONNECT tunnel: HTTP/1.1 negotiated
    > CONNECT api.example.net:80 HTTP/1.1
    < HTTP/1.1 200 Connection established
    * CONNECT phase completed
    * CONNECT tunnel established, response 200

    Successful tunneling is indicated by a visible CONNECT request line, a 2xx response such as 200 Connection established from the proxy, and subsequent application traffic flowing through the tunnel.