Repository: xroche/httrack (2026-07-16T06:28:19Z)

Issue:
Plain http through a CONNECT-only proxy (tor HTTPTunnelPort) is refused
## Symptom

Mirroring a plain `http://` site through a tor daemon fails, while `https://` sites through the same proxy work. The failure looks like a broken or refusing proxy, so the natural conclusion is that the proxy setting is ignored, which sends people looking in the wrong place.

## Cause

Since #85, https goes to the proxy as `CONNECT host:443`, and that is the only method tor's HTTP tunnel accepts. For `http://` targets we still use the classic absolute URI form, `GET http://host/path HTTP/1.1`, built at htslib.c:1147 with the `Proxy-Authorization` header added at htslib.c:1192.

A tor daemon's `HTTPTunnelPort` is a CONNECT-only proxy. The Tor spec is explicit that such a port "SHOULD NOT support any other HTTP methods" (https://spec.torproject.org/http-connect.html). So the https path goes through and the http path is refused, on the same proxy, with the same configuration.

This is not tor-specific. Any CONNECT-only proxy behaves the same way, and CONNECT-only is the shape a tunneling proxy naturally has.

## Proposal

An option to tunnel plain http through CONNECT as well: open `CONNECT host:80`, then send the request in origin form over the tunnel, with no `Proxy-Authorization` on the tunneled request, exactly as the https path does today.

It cannot be the default. Plenty of HTTP proxies restrict CONNECT to port 443 and would refuse `CONNECT host:80`, so flipping this on for everyone would break the common case to fix the tunnel case.

Auto-detection (fall back to CONNECT when the absolute URI request comes back 405 or 501) is tempting and I would rather not: proxies disagree on what they return for a method they dislike, and a retry that guesses wrong is worse than an option the user sets once.

## Implementation

The same three places that #563 touches, which is why the two are worth doing together even though either stands alone:

* htsback.c:2720 calls `http_proxy_tunnel()` only when the connection is TLS. It would also have to fire for plain http when this option is on, with 80 as the CONNECT port instead of 443. `http_proxy_tunnel()` already defaults the port to 443 when the address carries none, so the port needs to come from the scheme rather than being hardcoded.
* htslib.c:1147, the absolute URI request line, has to fall back to origin form when the request is tunneled.
* htslib.c:1192, the `Proxy-Authorization` header, has to be omitted for the same reason it already is on https: over a tunnel it goes to the origin, not to the proxy, and the credentials belong on the CONNECT.

A test would need a CONNECT proxy in front of a plain http origin, which is `tests/proxy-https-server.py` with the origin swapped, in the shape of `13_crawl_proxy_https.test`.



Discussion:

Patch:
diff --git a/html/server/option10.html b/html/server/option10.html
index 540f086d1..feff629a6 100644
--- a/html/server/option10.html
+++ b/html/server/option10.html
@@ -103,6 +103,7 @@ <h2 align="center"><em>${LANG_O2} - ${LANG_IOPT10}</em></h2>
 			title='${html:LANG_PROXYTYPETIP}' onMouseOver="info('${html:LANG_PROXYTYPETIP}'); return true" onMouseOut="info('&nbsp;'); return true"
 >
 <option value=""${ztest:proxytype: selected:}>HTTP</option>
+<option value="2"${test:proxytype::: selected}>HTTP (CONNECT tunnel)</option>
 <option value="1"${test:proxytype:: selected}>SOCKS5</option>
 </select>
 <br><br>
diff --git a/html/server/step4.html b/html/server/step4.html
index 204159704..524a75498 100644
--- a/html/server/step4.html
+++ b/html/server/step4.html
@@ -192,7 +192,7 @@ <h2 align="center"><em>Start</em></h2>
 	${test:logtype:::--extra-log:--debug-log}
 	${test:index:--index=0:}
 	${test:index2:--search-index=0:--search-index}
-	${test:prox:--proxy "}${do:if-not-empty:prox}${test:proxytype:socks5\3A//}${do:end-if}${do:output-mode:html}${prox}${test:prox:\3A}${portprox}${test:prox:"}
+	${test:prox:--proxy "}${do:if-not-empty:prox}${test:proxytype::socks5:connect}${test:proxytype:\3A//}${do:end-if}${do:output-mode:html}${prox}${test:prox:\3A}${portprox}${test:prox:"}
 	${test:ftpprox:--httpproxy-ftp=0:--httpproxy-ftp}
 </textarea>
 
diff --git a/lang.def b/lang.def
index db9f744b2..ac4a01832 100755
--- a/lang.def
+++ b/lang.def
@@ -1009,7 +1009,7 @@ A fatal error has occurred during this mirror
 LANG_PROXYTYPE
 Proxy type:
 LANG_PROXYTYPETIP
-Proxy protocol: HTTP, or SOCKS5 (SOCKS5 default port is 1080).
+Proxy protocol. HTTP: standard proxy. HTTP (CONNECT tunnel): sends every request through a CONNECT tunnel, for CONNECT-only proxies like Tor's HTTPTunnelPort. SOCKS5: default port 1080.
 LANG_COOKIEFILE
 Load cookies from file:
 LANG_COOKIEFILETIP
diff --git a/lang/Bulgarian.txt b/lang/Bulgarian.txt
index fd90e2c2d..7a839af2a 100644
--- a/lang/Bulgarian.txt
+++ b/lang/Bulgarian.txt
@@ -930,8 +930,8 @@ A fatal error has occurred during this mirror
 ������� ������ ��� ����������� �� ���� ��������� ����
 Proxy type:
 ��� �� ������:
-Proxy protocol: HTTP, or SOCKS5 (SOCKS5 default port is 1080).
-�������� �� ������: HTTP ��� SOCKS5 (������������ ���� �� SOCKS5 � 1080).
+Proxy protocol. HTTP: standard proxy. HTTP (CONNECT tunnel): sends every request through a CONNECT tunnel, for CONNECT-only proxies like Tor's HTTPTunnelPort. SOCKS5: default port 1080.
+�������� �� ������. HTTP: ���������� ������. HTTP (CONNECT �����): ������� ����� ������ ���� CONNECT �����, �� ������ ���������� ���� CONNECT ���� HTTPTunnelPort �� Tor. SOCKS5: ���� �� ������������ 1080.
 Load cookies from file:
 ��������� �� '���������' �� ����:
 Preload cookies from a Netscape cookies.txt file before crawling.
diff --git a/lang/Castellano.txt b/lang/Castellano.txt
index a10a45f76..cec6aeedd 100644
--- a/lang/Castellano.txt
+++ b/lang/Castellano.txt
@@ -930,8 +930,8 @@ A fatal error has occurred during this mirror
 Ha ocurrido un error fatal durante esta copia
 Proxy type:
 Tipo de proxy:
-Proxy protocol: HTTP, or SOCKS5 (SOCKS5 default port is 1080).
-Protocolo del proxy: HTTP o SOCKS5 (el puerto SOCKS5 predeterminado es 1080).
+Proxy protocol. HTTP: standard proxy. HTTP (CONNECT tunnel): sends every request through a CONNECT tunnel, for CONNECT-only proxies like Tor's HTTPTunnelPort. SOCKS5: default port 1080.
+Protocolo del proxy. HTTP: proxy est�ndar. HTTP (t�nel CONNECT): env�a cada petici�n a trav�s de un t�nel CONNECT, para proxys que solo admiten CONNECT como el HTTPTunnelPort de Tor. SOCKS5: puerto predeterminado 1080.
 Load cookies from file:
 Cargar cookies desde un archivo:
 Preload cookies from a Netscape cookies.txt file before crawling.
diff --git a/lang/Cesky.txt b/lang/Cesky.txt
index cead81e4c..847c49692 100644
--- a/lang/Cesky.txt
+++ b/lang/Cesky.txt
@@ -930,8 +930,8 @@ A fatal error has occurred during this mirror
 
 Proxy type:
 Typ proxy:
-Proxy protocol: HTTP, or SOCKS5 (SOCKS5 default port is 1080).
-Protokol proxy: HTTP nebo SOCKS5 (v�choz� port SOCKS5 je 1080).
+Proxy protocol. HTTP: standard proxy. HTTP (CONNECT tunnel): sends every request through a CONNECT tunnel, for CONNECT-only proxies like Tor's HTTPTunnelPort. SOCKS5: default port 1080.
+Protokol proxy. HTTP: standardn� proxy. HTTP (tunel CONNECT): ode�le ka�d� po�adavek p�es tunel CONNECT, pro proxy podporuj�c� jen CONNECT jako HTTPTunnelPort v Toru. SOCKS5: v�choz� port 1080.
 Load cookies from file:
 Na��st cookies ze souboru:
 Preload cookies from a Netscape cookies.txt file before crawling.
diff --git a/lang/Chinese-BIG5.txt b/lang/Chinese-BIG5.txt
index f92a341d8..1fc949ee1 100644
--- a/lang/Chinese-BIG5.txt
+++ b/lang/Chinese-BIG5.txt
@@ -930,8 +930,8 @@ A fatal error has occurred during this mirror
 �o�蹳�o�ͤF���i�^�_�����~
 Proxy type:
 proxy ����:
-Proxy protocol: HTTP, or SOCKS5 (SOCKS5 default port is 1080).
-proxy ��w: HTTP �� SOCKS5 (SOCKS5 ���w�]�s���� 1080)�C
+Proxy protocol. HTTP: standard proxy. HTTP (CONNECT tunnel): sends every request through a CONNECT tunnel, for CONNECT-only proxies like Tor's HTTPTunnelPort. SOCKS5: default port 1080.
+�N�z��w�CHTTP�G�зǥN�z�CHTTP�]CONNECT �G�D�^�G�z�L CONNECT �G�D�ǰe�C�ӽШD�A�Ω�Ȥ䴩 CONNECT ���N�z�A�Ҧp Tor �� HTTPTunnelPort�CSOCKS5�G�w�]�s���� 1080�C
 Load cookies from file:
 �q�ɮ׸��J cookies:
 Preload cookies from a Netscape cookies.txt file before crawling.
diff --git a/lang/Chinese-Simplified.txt b/lang/Chinese-Simplified.txt
index ef75c92bc..3546fc745 100644
--- a/lang/Chinese-Simplified.txt
+++ b/lang/Chinese-Simplified.txt
@@ -930,8 +930,8 @@ A fatal error has occurred during this mirror
 
 Proxy type:
 ��������:
-Proxy protocol: HTTP, or SOCKS5 (SOCKS5 default port is 1080).
-����Э��: HTTP �� SOCKS5 (SOCKS5 ��Ĭ�϶˿�Ϊ 1080)��
+Proxy protocol. HTTP: standard proxy. HTTP (CONNECT tunnel): sends every request through a CONNECT tunnel, for CONNECT-only proxies like Tor's HTTPTunnelPort. SOCKS5: default port 1080.
+����Э�顣HTTP����׼������HTTP��CONNECT ��������ͨ�� CONNECT ��������ÿ���������ڽ�֧�� CONNECT �Ĵ��������� Tor �� HTTPTunnelPort��SOCKS5��Ĭ�϶˿� 1080��
 Load cookies from file:
 ���ļ����� cookies:
 Preload cookies from a Netscape cookies.txt file before crawling.
diff --git a/lang/Croatian.txt b/lang/Croatian.txt
index 40e574ae4..c5746fc07 100644
--- a/lang/Croatian.txt
+++ b/lang/Croatian.txt
@@ -932,8 +932,8 @@ A fatal error has occurred during this mirror
 Tijekom ovog zrcaljenja je nastala fatalna pogre�ka
 Proxy type:
 Vrsta posrednika:
-Proxy protocol: HTTP, or SOCKS5 (SOCKS5 default port is 1080).
-Protokol posrednika: HTTP ili SOCKS5 (zadani port za SOCKS5 je 1080).
+Proxy protocol. HTTP: standard proxy. HTTP (CONNECT tunnel): sends every request through a CONNECT tunnel, for CONNECT-only proxies like Tor's HTTPTunnelPort. SOCKS5: default port 1080.
+Protokol posrednika. HTTP: standardni posrednik. HTTP (CONNECT tunel): �alje svaki zahtjev kroz CONNECT tunel, za posrednike koji podr�avaju samo CONNECT poput Torovog HTTPTunnelPorta. SOCKS5: zadani port 1080.
 Load cookies from file:
 U�itati kola�i�e iz datoteke:
 Preload cookies from a Netscape cookies.txt file before crawling.
diff --git a/lang/Dansk.txt b/lang/Dansk.txt
index 7fb318839..18b66231d 100644
--- a/lang/Dansk.txt
+++ b/lang/Dansk.txt
@@ -978,8 +978,8 @@ HTTrack: could not save profile for '%s'!
 HTTrack: kunne ikke gemme profil for '%s'!
 Proxy type:
 Proxytype:
-Proxy protocol: HTTP, or SOCKS5 (SOCKS5 default port is 1080).
-Proxyprotokol: HTTP eller SOCKS5 (SOCKS5 bruger som standard port 1080).
+Proxy protocol. HTTP: standard proxy. HTTP (CONNECT tunnel): sends every request through a CONNECT tunnel, for CONNECT-only proxies like Tor's HTTPTunnelPort. SOCKS5: default port 1080.
+Proxyprotokol. HTTP: standardproxy. HTTP (CONNECT-tunnel): sender hver foresp�rgsel gennem en CONNECT-tunnel, til proxyer der kun underst�tter CONNECT som Tors HTTPTunnelPort. SOCKS5: standardport 1080.
 Load cookies from file:
 Indl�s cookies fra fil:
 Preload cookies from a Netscape cookies.txt file before crawling.
diff --git a/lang/Deutsch.txt b/lang/Deutsch.txt
index e4d05ce11..7cd6b7374 100644
--- a/lang/Deutsch.txt
+++ b/lang/Deutsch.txt
@@ -930,8 +930,8 @@ A fatal error has occurred during this mirror
 Fataler Fehler w�hrend der Webseiten-Kopie
 Proxy type:
 Proxy-Typ:
-Proxy protocol: HTTP, or SOCKS5 (SOCKS5 default port is 1080).
-Proxy-Protokoll: HTTP oder SOCKS5 (Standardport von SOCKS5 ist 1080).
+Proxy protocol. HTTP: standard proxy. HTTP (CONNECT tunnel): sends every request through a CONNECT tunnel, for CONNECT-only proxies like Tor's HTTPTunnelPort. SOCKS5: default port 1080.
+Proxy-Protokoll. HTTP: normaler Proxy. HTTP (CONNECT-Tunnel): sendet jede Anfrage durch einen CONNECT-Tunnel, f�r Proxys, die nur CONNECT unterst�tzen, wie Tors HTTPTunnelPort. SOCKS5: Standardport 1080.
 Load cookies from file:
 Cookies aus Datei laden:
 Preload cookies from a Netscape cookies.txt file before crawling.
diff --git a/lang/Eesti.txt b/lang/Eesti.txt
index 1b3dce37c..bb297ddc3 100644
--- a/lang/Eesti.txt
+++ b/lang/Eesti.txt
@@ -930,8 +930,8 @@ A fatal error has occurred during this mirror
 
 Proxy type:
 Proxy t��p:
-Proxy protocol: HTTP, or SOCKS5 (SOCKS5 default port is 1080).
-Proxy protokoll: HTTP v�i SOCKS5 (SOCKS5 vaikeport on 1080).
+Proxy protocol. HTTP: standard proxy. HTTP (CONNECT tunnel): sends every request through a CONNECT tunnel, for CONNECT-only proxies like Tor's HTTPTunnelPort. SOCKS5: default port 1080.
+Puhverserveri protokoll. HTTP: tavaline puhverserver. HTTP (CONNECT-tunnel): saadab iga p�ringu l�bi CONNECT-tunneli, ainult CONNECT-it toetavate puhverserverite jaoks nagu Tori HTTPTunnelPort. SOCKS5: vaikeport 1080.
 Load cookies from file:
 Lae k�psised failist:
 Preload cookies from a Netscape cookies.txt file before crawling.
diff --git a/lang/English.txt b/lang/English.txt
index 90aebb644..ac6249b22 100644
--- a/lang/English.txt
+++ b/lang/English.txt
@@ -978,8 +978,8 @@ HTTrack: could not save profile for '%s'!
 HTTrack: could not save profile for '%s'!
 Proxy type:
 Proxy type:
-Proxy protocol: HTTP, or SOCKS5 (SOCKS5 default port is 1080).
-Proxy protocol: HTTP, or SOCKS5 (SOCKS5 default port is 1080).
+Proxy protocol. HTTP: standard proxy. HTTP (CONNECT tunnel): sends every request through a CONNECT tunnel, for CONNECT-only proxies like Tor's HTTPTunnelPort. SOCKS5: default port 1080.
+Proxy protocol. HTTP: standard proxy. HTTP (CONNECT tunnel): sends every request through a CONNECT tunnel, for CONNECT-only proxies like Tor's HTTPTunnelPort. SOCKS5: default port 1080.
 Load cookies from file:
 Load cookies from file:
 Preload cookies from a Netscape cookies.txt file before crawling.
diff --git a/lang/Finnish.txt b/lang/Finnish.txt
index d53c20521..6a9e97627 100644
--- a/lang/Finnish.txt
+++ b/lang/Finnish.txt
@@ -933,8 +933,8 @@ T
 
 Proxy type:
 V�lityspalvelimen tyyppi:
-Proxy protocol: HTTP, or SOCKS5 (SOCKS5 default port is 1080).
-V�lityspalvelimen protokolla: HTTP tai SOCKS5 (SOCKS5:n oletusportti on 1080).
+Proxy protocol. HTTP: standard proxy. HTTP (CONNECT tunnel): sends every request through a CONNECT tunnel, for CONNECT-only proxies like Tor's HTTPTunnelPort. SOCKS5: default port 1080.
+V�lityspalvelimen protokolla. HTTP: tavallinen v�lityspalvelin. HTTP (CONNECT-tunneli): l�hett�� jokaisen pyynn�n CONNECT-tunnelin kautta, vain CONNECT-yhteyksi� tukeville v�lityspalvelimille kuten Torin HTTPTunnelPort. SOCKS5: oletusportti 1080.
 Load cookies from file:
 Lataa ev�steet tiedostosta:
 Preload cookies from a Netscape cookies.txt file before crawling.
diff --git a/lang/Francais.txt b/lang/Francais.txt
index 2dfa06a5a..ae1379e3e 100644
--- a/lang/Francais.txt
+++ b/lang/Francais.txt
@@ -978,8 +978,8 @@ Default referer URL
 Champ referer par d�faut
 Proxy type:
 Type de proxy :
-Proxy protocol: HTTP, or SOCKS5 (SOCKS5 default port is 1080).
-Protocole du proxy : HTTP ou SOCKS5 (le port SOCKS5 par d�faut est 1080).
+Proxy protocol. HTTP: standard proxy. HTTP (CONNECT tunnel): sends every request through a CONNECT tunnel, for CONNECT-only proxies like Tor's HTTPTunnelPort. SOCKS5: default port 1080.
+Protocole du proxy. HTTP : proxy standard. HTTP (tunnel CONNECT) : envoie chaque requ�te via un tunnel CONNECT, pour les proxys acceptant uniquement CONNECT comme le HTTPTunnelPort de Tor. SOCKS5 : port par d�faut 1080.
 Load cookies from file:
 Charger les cookies depuis un fichier :
 Preload cookies from a Netscape cookies.txt file before crawling.
diff --git a/lang/Greek.txt b/lang/Greek.txt
index e50f1983c..663f55633 100644
--- a/lang/Greek.txt
+++ b/lang/Greek.txt
@@ -932,8 +932,8 @@ A fatal error has occurred during this mirror
 ��� ������������ ������ ���������� ���� ��� ��������� ����� ��� �����
 Proxy type:
 ����� proxy:
-Proxy protocol: HTTP, or SOCKS5 (SOCKS5 default port is 1080).
-���������� proxy: HTTP � SOCKS5 (� ������������� ���� ��� SOCKS5 ����� 1080).
+Proxy protocol. HTTP: standard proxy. HTTP (CONNECT tunnel): sends every request through a CONNECT tunnel, for CONNECT-only proxies like Tor's HTTPTunnelPort. SOCKS5: default port 1080.
+���������� �������������. HTTP: ������� ��������������. HTTP (������� CONNECT): ������� ���� ������ ���� �������� CONNECT, ��� �������������� ��� ������������ ���� CONNECT ���� �� HTTPTunnelPort ��� Tor. SOCKS5: ������������� ���� 1080.
 Load cookies from file:
 ������� cookies ��� ������:
 Preload cookies from a Netscape cookies.txt file before crawling.
diff --git a/lang/Italiano.txt b/lang/Italiano.txt
index 039751556..900727e45 100644
--- a/lang/Italiano.txt
+++ b/lang/Italiano.txt
@@ -930,8 +930,8 @@ A fatal error has occurred during this mirror
 Si � verificato un errore fatale durante la copia
 Proxy type:
 Tipo di proxy:
-Proxy protocol: HTTP, or SOCKS5 (SOCKS5 default port is 1080).
-Protocollo del proxy: HTTP o SOCKS5 (la porta SOCKS5 predefinita � 1080).
+Proxy protocol. HTTP: standard proxy. HTTP (CONNECT tunnel): sends every request through a CONNECT tunnel, for CONNECT-only proxies like Tor's HTTPTunnelPort. SOCKS5: default port 1080.
+Protocollo del proxy. HTTP: proxy standard. HTTP (tunnel CONNECT): invia ogni richiesta attraverso un tunnel CONNECT, per proxy che supportano solo CONNECT come l'HTTPTunnelPort di Tor. SOCKS5: porta predefinita 1080.
 Load cookies from file:
 Carica i cookie da un file:
 Preload cookies from a Netscape cookies.txt file before crawling.
diff --git a/lang/Japanese.txt b/lang/Japanese.txt
index d9cdb42b2..ccb31fc29 100644
--- a/lang/Japanese.txt
+++ b/lang/Japanese.txt
@@ -930,8 +930,8 @@ A fatal error has occurred during this mirror
 
 Proxy type:
 �v���L�V�̎��:
-Proxy protocol: HTTP, or SOCKS5 (SOCKS5 default port is 1080).
-�v���L�V�̃v���g�R��: HTTP �܂��� SOCKS5 (SOCKS5 �̊���|�[�g�� 1080)�B
+Proxy protocol. HTTP: standard proxy. HTTP (CONNECT tunnel): sends every request through a CONNECT tunnel, for CONNECT-only proxies like Tor's HTTPTunnelPort. SOCKS5: default port 1080.
+�v���L�V�̃v���g�R���BHTTP: �W���̃v���L�V�BHTTP (CONNECT �g���l��): ���ׂẴ��N�G�X�g�� CONNECT �g���l���o�R�ő��M���܂��BTor �� HTTPTunnelPort �̂悤�� CONNECT �݂̂̃v���L�V�p�ł��BSOCKS5: ����|�[�g 1080�B
 Load cookies from file:
 �N�b�L�[���t�@�C������ǂݍ���:
 Preload cookies from a Netscape cookies.txt file before crawling.
diff --git a/lang/Macedonian.txt b/lang/Macedonian.txt
index 4ceecf8ef..b61648625 100644
--- a/lang/Macedonian.txt
+++ b/lang/Macedonian.txt
@@ -930,8 +930,8 @@ A fatal error has occurred during this mirror
 ������� ������� ������ ��� ��� mirror
 Proxy type:
 ��� �� ������:
-Proxy protocol: HTTP, or SOCKS5 (SOCKS5 default port is 1080).
-�������� �� ������: HTTP ��� SOCKS5 (������������ ����� �� SOCKS5 � 1080).
+Proxy protocol. HTTP: standard proxy. HTTP (CONNECT tunnel): sends every request through a CONNECT tunnel, for CONNECT-only proxies like Tor's HTTPTunnelPort. SOCKS5: default port 1080.
+�������� �� ������. HTTP: ���������� ������. HTTP (����� CONNECT): �� ������� ����� ������ ����� ����� CONNECT, �� ������ ��� ����������� ���� CONNECT ���� HTTPTunnelPort �� Tor. SOCKS5: ���������� ����� 1080.
 Load cookies from file:
 ������ �������� �� ��������:
 Preload cookies from a Netscape cookies.txt file before crawling.
diff --git a/lang/Magyar.txt b/lang/Magyar.txt
index a2df460ab..382e14817 100644
--- a/lang/Magyar.txt
+++ b/lang/Magyar.txt
@@ -930,8 +930,8 @@ A fatal error has occurred during this mirror
 V�gzetes hiba t�rt�nt a t�kr�z�s k�zben
 Proxy type:
 Proxy t�pusa:
-Proxy protocol: HTTP, or SOCKS5 (SOCKS5 default port is 1080).
-Proxy protokoll: HTTP vagy SOCKS5 (a SOCKS5 alap�rtelmezett portja 1080).
+Proxy protocol. HTTP: standard proxy. HTTP (CONNECT tunnel): sends every request through a CONNECT tunnel, for CONNECT-only proxies like Tor's HTTPTunnelPort. SOCKS5: default port 1080.
+Proxy protokoll. HTTP: szabv�nyos proxy. HTTP (CONNECT alag�t): minden k�r�st CONNECT alag�ton kereszt�l k�ld, a csak CONNECT-et t�mogat� proxykhoz, mint a Tor HTTPTunnelPortja. SOCKS5: alap�rtelmezett port 1080.
 Load cookies from file:
 S�tik bet�lt�se f�jlb�l:
 Preload cookies from a Netscape cookies.txt file before crawling.
diff --git a/lang/Nederlands.txt b/lang/Nederlands.txt
index fc711d7e2..dc72ef240 100644
--- a/lang/Nederlands.txt
+++ b/lang/Nederlands.txt
@@ -930,8 +930,8 @@ A fatal error has occurred during this mirror
 Een fatale fout is opgetreden tijdens deze spiegeling
 Proxy type:
 Proxytype:
-Proxy protocol: HTTP, or SOCKS5 (SOCKS5 default port is 1080).
-Proxyprotocol: HTTP of SOCKS5 (standaardpoort van SOCKS5 is 1080).
+Proxy protocol. HTTP: standard proxy. HTTP (CONNECT tunnel): sends every request through a CONNECT tunnel, for CONNECT-only proxies like Tor's HTTPTunnelPort. SOCKS5: default port 1080.
+Proxyprotocol. HTTP: standaardproxy. HTTP (CONNECT-tunnel): stuurt elke aanvraag via een CONNECT-tunnel, voor proxy's die alleen CONNECT ondersteunen zoals Tors HTTPTunnelPort. SOCKS5: standaardpoort 1080.
 Load cookies from file:
 Cookies uit bestand laden:
 Preload cookies from a Netscape cookies.txt file before crawling.
diff --git a/lang/Norsk.txt b/lang/Norsk.txt
index 7a6b9e526..0fb0ba6cb 100644
--- a/lang/Norsk.txt
+++ b/lang/Norsk.txt
@@ -930,8 +930,8 @@ A fatal error has occurred during this mirror
 
 Proxy type:
 Proxytype:
-Proxy protocol: HTTP, or SOCKS5 (SOCKS5 default port is 1080).
-Proxyprotokoll: HTTP eller SOCKS5 (SOCKS5 bruker som standard port 1080).
+Proxy protocol. HTTP: standard proxy. HTTP (CONNECT tunnel): sends every request through a CONNECT tunnel, for CONNECT-only proxies like Tor's HTTPTunnelPort. SOCKS5: default port 1080.
+Proxyprotokoll. HTTP: standard proxy. HTTP (CONNECT-tunnel): sender hver foresp�rsel gjennom en CONNECT-tunnel, for proxyer som bare st�tter CONNECT som Tors HTTPTunnelPort. SOCKS5: standardport 1080.
 Load cookies from file:
 Last inn cookies fra fil:
 Preload cookies from a Netscape cookies.txt file before crawling.
diff --git a/lang/Polski.txt b/lang/Polski.txt
index 21554d4f8..5da090029 100644
--- a/lang/Polski.txt
+++ b/lang/Polski.txt
@@ -930,8 +930,8 @@ A fatal error has occurred during this mirror
 Podczas tworzenia lustra wydarzyl sie fatalny blad.
 Proxy type:
 Typ proxy:
-Proxy protocol: HTTP, or SOCKS5 (SOCKS5 default port is 1080).
-Protok� proxy: HTTP lub SOCKS5 (domy�lny port SOCKS5 to 1080).
+Proxy protocol. HTTP: standard proxy. HTTP (CONNECT tunnel): sends every request through a CONNECT tunnel, for CONNECT-only proxies like Tor's HTTPTunnelPort. SOCKS5: default port 1080.
+Protok� proxy. HTTP: standardowe proxy. HTTP (tunel CONNECT): wysy�a ka�de ��danie przez tunel CONNECT, dla proxy obs�uguj�cych tylko CONNECT, jak HTTPTunnelPort w Torze. SOCKS5: port domy�lny 1080.
 Load cookies from file:
 Wczytaj ciasteczka z pliku:
 Preload cookies from a Netscape cookies.txt file before crawling.
diff --git a/lang/Portugues-Brasil.txt b/lang/Portugues-Brasil.txt
index 51949395e..9d38e4770 100644
--- a/lang/Portugues-Brasil.txt
+++ b/lang/Portugues-Brasil.txt
@@ -978,8 +978,8 @@ HTTrack: could not save profile for '%s'!
 HTTrack: n�o foi poss�vel salvar o perfil para '%s'!
 Proxy type:
 Tipo de proxy:
-Proxy protocol: HTTP, or SOCKS5 (SOCKS5 default port is 1080).
-Protocolo do proxy: HTTP ou SOCKS5 (a porta SOCKS5 padr�o � 1080).
+Proxy protocol. HTTP: standard proxy. HTTP (CONNECT tunnel): sends every request through a CONNECT tunnel, for CONNECT-only proxies like Tor's HTTPTunnelPort. SOCKS5: default port 1080.
+Protocolo do proxy. HTTP: proxy padr�o. HTTP (t�nel CONNECT): envia cada requisi��o atrav�s de um t�nel CONNECT, para proxies que s� suportam CONNECT como o HTTPTunnelPort do Tor. SOCKS5: porta padr�o 1080.
 Load cookies from file:
 Carregar cookies de um arquivo:
 Preload cookies from a Netscape cookies.txt file before crawling.
diff --git a/lang/Portugues.txt b/lang/Portugues.txt
index e8080c0c4..6b84c857f 100644
--- a/lang/Portugues.txt
+++ b/lang/Portugues.txt
@@ -930,8 +930,8 @@ A fatal error has occurred during this mirror
 
 Proxy type:
 Tipo de proxy:
-Proxy protocol: HTTP, or SOCKS5 (SOCKS5 default port is 1080).
-Protocolo do proxy: HTTP ou SOCKS5 (a porta SOCKS5 predefinida � 1080).
+Proxy protocol. HTTP: standard proxy. HTTP (CONNECT tunnel): sends every request through a CONNECT tunnel, for CONNECT-only proxies like Tor's HTTPTunnelPort. SOCKS5: default port 1080.
+Protocolo do proxy. HTTP: proxy padr�o. HTTP (t�nel CONNECT): envia cada pedido atrav�s de um t�nel CONNECT, para proxies que s� suportam CONNECT como o HTTPTunnelPort do Tor. SOCKS5: porta predefinida 1080.
 Load cookies from file:
 Carregar cookies de um ficheiro:
 Preload cookies from a Netscape cookies.txt file before crawling.
diff --git a/lang/Romanian.txt b/lang/Romanian.txt
index bcb682e46..5e556a32c 100644
--- a/lang/Romanian.txt
+++ b/lang/Romanian.txt
@@ -930,8 +930,8 @@ A fatal error has occurred during this mirror
 A survenit o eroare fatal� �n timpul acestei clon�ri.
 Proxy type:
 Tip proxy:
-Proxy protocol: HTTP, or SOCKS5 (SOCKS5 default port is 1080).
-Protocol proxy: HTTP sau SOCKS5 (portul SOCKS5 implicit este 1080).
+Proxy protocol. HTTP: standard proxy. HTTP (CONNECT tunnel): sends every request through a CONNECT tunnel, for CONNECT-only proxies like Tor's HTTPTunnelPort. SOCKS5: default port 1080.
+Protocol proxy. HTTP: proxy standard. HTTP (tunel CONNECT): trimite fiecare cerere printr-un tunel CONNECT, pentru proxy care accepta doar CONNECT precum HTTPTunnelPort al lui Tor. SOCKS5: port implicit 1080.
 Load cookies from file:
 �ncarca cookies dintr-un fisier:
 Preload cookies from a Netscape cookies.txt file before crawling.
diff --git a/lang/Russian.txt b/lang/Russian.txt
index d5201bc27..861484ca7 100644
--- a/lang/Russian.txt
+++ b/lang/Russian.txt
@@ -930,8 +930,8 @@ A fatal error has occurred during this mirror
 �� ����� ������� ������� ��������� ��������� ������
 Proxy type:
 ��� ������:
-Proxy protocol: HTTP, or SOCKS5 (SOCKS5 default port is 1080).
-�������� ������: HTTP ��� SOCKS5 (�� ��������� SOCKS5 ���������� ���� 1080).
+Proxy protocol. HTTP: standard proxy. HTTP (CONNECT tunnel): sends every request through a CONNECT tunnel, for CONNECT-only proxies like Tor's HTTPTunnelPort. SOCKS5: default port 1080.
+�������� ������. HTTP: ������� ������. HTTP (������� CONNECT): ���������� ������ ������ ����� ������� CONNECT, ��� ������, �������������� ������ CONNECT, �������� HTTPTunnelPort � Tor. SOCKS5: ���� �� ��������� 1080.
 Load cookies from file:
 ��������� cookies �� �����:
 Preload cookies from a Netscape cookies.txt file before crawling.
diff --git a/lang/Slovak.txt b/lang/Slovak.txt
index 7873f3750..0a426a3d6 100644
--- a/lang/Slovak.txt
+++ b/lang/Slovak.txt
@@ -930,8 +930,8 @@ A fatal error has occurred during this mirror
 
 Proxy type:
 Typ proxy:
-Proxy protocol: HTTP, or SOCKS5 (SOCKS5 default port is 1080).
-Protokol proxy: HTTP alebo SOCKS5 (predvolen� port SOCKS5 je 1080).
+Proxy protocol. HTTP: standard proxy. HTTP (CONNECT tunnel): sends every request through a CONNECT tunnel, for CONNECT-only proxies like Tor's HTTPTunnelPort. SOCKS5: default port 1080.
+Protokol proxy. HTTP: �tandardn� proxy. HTTP (tunel CONNECT): odo�le ka�d� po�iadavku cez tunel CONNECT, pre proxy podporuj�ce len CONNECT ako HTTPTunnelPort v Tore. SOCKS5: predvolen� port 1080.
 Load cookies from file:
 Na��ta� cookies zo s�boru:
 Preload cookies from a Netscape cookies.txt file before crawling.
diff --git a/lang/Slovenian.txt b/lang/Slovenian.txt
index 3021677d4..c4fbd72f8 100644
--- a/lang/Slovenian.txt
+++ b/lang/Slovenian.txt
@@ -930,8 +930,8 @@ A fatal error has occurred during this mirror
 
 Proxy type:
 Vrsta proxyja:
-Proxy protocol: HTTP, or SOCKS5 (SOCKS5 default port is 1080).
-Protokol proxy: HTTP ali SOCKS5 (privzeta vrata SOCKS5 so 1080).
+Proxy protocol. HTTP: standard proxy. HTTP (CONNECT tunnel): sends every request through a CONNECT tunnel, for CONNECT-only proxies like Tor's HTTPTunnelPort. SOCKS5: default port 1080.
+Protokol proxy. HTTP: standardni proxy. HTTP (tunel CONNECT): poslje vsako zahtevo skozi tunel CONNECT, za posrednike, ki podpirajo samo CONNECT, kot je HTTPTunnelPort v Toru. SOCKS5: privzeta vrata 1080.
 Load cookies from file:
 Nalozi piskotke iz datoteke:
 Preload cookies from a Netscape cookies.txt file before crawling.
diff --git a/lang/Svenska.txt b/lang/Svenska.txt
index f8928c2a3..9c8e56e61 100644
--- a/lang/Svenska.txt
+++ b/lang/Svenska.txt
@@ -930,8 +930,8 @@ A fatal error has occurred during this mirror
 
 Proxy type:
 Proxytyp:
-Proxy protocol: HTTP, or SOCKS5 (SOCKS5 default port is 1080).
-Proxyprotokoll: HTTP eller SOCKS5 (SOCKS5 anv�nder som standard port 1080).
+Proxy protocol. HTTP: standard proxy. HTTP (CONNECT tunnel): sends every request through a CONNECT tunnel, for CONNECT-only proxies like Tor's HTTPTunnelPort. SOCKS5: default port 1080.
+Proxyprotokoll. HTTP: standardproxy. HTTP (CONNECT-tunnel): skickar varje beg�ran genom en CONNECT-tunnel, f�r proxyservrar som bara st�der CONNECT som Tors HTTPTunnelPort. SOCKS5: standardport 1080.
 Load cookies from file:
 L�s in cookies fr�n fil:
 Preload cookies from a Netscape cookies.txt file before crawling.
diff --git a/lang/Turkish.txt b/lang/Turkish.txt
index 9ee63bff3..22580fbb3 100644
--- a/lang/Turkish.txt
+++ b/lang/Turkish.txt
@@ -930,8 +930,8 @@ A fatal error has occurred during this mirror
 Bu yans�lama i�lemi s�ras�nda �l�mc�l bir hata olu�tu
 Proxy type:
 Proxy t�r�:
-Proxy protocol: HTTP, or SOCKS5 (SOCKS5 default port is 1080).
-Proxy protokol�: HTTP veya SOCKS5 (SOCKS5 varsay�lan portu 1080).
+Proxy protocol. HTTP: standard proxy. HTTP (CONNECT tunnel): sends every request through a CONNECT tunnel, for CONNECT-only proxies like Tor's HTTPTunnelPort. SOCKS5: default port 1080.
+Proxy protokol�. HTTP: standart proxy. HTTP (CONNECT t�neli): her iste�i bir CONNECT t�neli �zerinden g�nderir; Tor'un HTTPTunnelPort gibi yaln�zca CONNECT destekleyen proxy'ler i�in. SOCKS5: varsay�lan ba�lant� noktas� 1080.
 Load cookies from file:
 �erezleri dosyadan y�kle:
 Preload cookies from a Netscape cookies.txt file before crawling.
diff --git a/lang/Ukrainian.txt b/lang/Ukrainian.txt
index ed540e487..2dec1bca7 100644
--- a/lang/Ukrainian.txt
+++ b/lang/Ukrainian.txt
@@ -930,8 +930,8 @@ A fatal error has occurred during this mirror
 
 Proxy type:
 ��� �����:
-Proxy protocol: HTTP, or SOCKS5 (SOCKS5 default port is 1080).
-�������� �����: HTTP ��� SOCKS5 (�� ������������� SOCKS5 ����������� ���� 1080).
+Proxy protocol. HTTP: standard proxy. HTTP (CONNECT tunnel): sends every request through a CONNECT tunnel, for CONNECT-only proxies like Tor's HTTPTunnelPort. SOCKS5: default port 1080.
+�������� �����. HTTP: ����������� �����. HTTP (������ CONNECT): ������� ����� ����� ����� ������ CONNECT, ��� �����, �� ���������� ���� CONNECT, ��-�� HTTPTunnelPort � Tor. SOCKS5: ���� �� ������������� 1080.
 Load cookies from file:
 ����������� cookies � �����:
 Preload cookies from a Netscape cookies.txt file before crawling.
diff --git a/lang/Uzbek.txt b/lang/Uzbek.txt
index 3e1588364..9773e1bea 100644
--- a/lang/Uzbek.txt
+++ b/lang/Uzbek.txt
@@ -930,8 +930,8 @@ A fatal error has occurred during this mirror
 Joriy ko�chirish vaqtida jiddiy xatolik yuz berdi
 Proxy type:
 Proksi turi:
-Proxy protocol: HTTP, or SOCKS5 (SOCKS5 default port is 1080).
-Proksi protokoli: HTTP yoki SOCKS5 (SOCKS5 ning standart porti 1080).
+Proxy protocol. HTTP: standard proxy. HTTP (CONNECT tunnel): sends every request through a CONNECT tunnel, for CONNECT-only proxies like Tor's HTTPTunnelPort. SOCKS5: default port 1080.
+Proksi protokoli. HTTP: oddiy proksi. HTTP (CONNECT tuneli): har bir so'rovni CONNECT tuneli orqali yuboradi; faqat CONNECT'ni qo'llab-quvvatlaydigan proksilar uchun, masalan Tor HTTPTunnelPort. SOCKS5: standart port 1080.
 Load cookies from file:
 Fayldan cookies yuklamoq:
 Preload cookies from a Netscape cookies.txt file before crawling.
diff --git a/man/httrack.1 b/man/httrack.1
index 66ca57269..96c3f3a09 100644
--- a/man/httrack.1
+++ b/man/httrack.1
@@ -135,7 +135,7 @@ continue an interrupted mirror using the cache (\-\-continue)
 mirror ALL links located in the first level pages (mirror links) (\-\-mirrorlinks)
 .SS Proxy options:
 .IP \-P
-proxy use (\-P [socks5://][user:pass@]proxy:port) (\-\-proxy <param>)
+proxy use (\-P [socks5://|connect://][user:pass@]proxy:port) (\-\-proxy <param>)
 .IP \-%f
 *use proxy for ftp (f0 don't use) (\-\-httpproxy\-ftp[=N])
 .IP \-%b
diff --git a/src/htsback.c b/src/htsback.c
index b5a9ef24e..fbfcf5782 100644
--- a/src/htsback.c
+++ b/src/htsback.c
@@ -2757,6 +2757,28 @@ void back_wait(struct_back * sback, httrackp * opt, cache_back * cache,
             }
           }
 
+          // plain http tunneled through a CONNECT-only proxy (#564)
+          if (back[i].r.req.proxy.active &&
+              hts_proxy_is_connect(back[i].r.req.proxy.name) &&
+              !back[i].r.keep_alive
+#if HTS_USEOPENSSL
+              && !back[i].r.ssl
+#endif
+          ) {
+            const int timeout = back[i].timeout > 0 ? back[i].timeout : 30;
+
+            if (!http_proxy_tunnel(opt, &back[i].r, back[i].url_adr, timeout)) {
+              if (!strnotempty(back[i].r.msg))
+                strcpybuff(back[i].r.msg, "proxy CONNECT failed");
+              deletehttp(&back[i].r);
+              back[i].r.soc = INVALID_SOCKET;
+              back[i].r.statuscode = STATUSCODE_NON_FATAL;
+              back[i].status = STATUS_READY;
+              back_set_finished(sback, i);
+              continue;
+            }
+          }
+
 #if HTS_USEOPENSSL
           /* SSL mode */
           if (back[i].r.ssl) {
diff --git a/src/htshelp.c b/src/htshelp.c
index 088dfa19d..9d3db084d 100644
--- a/src/htshelp.c
+++ b/src/htshelp.c
@@ -477,7 +477,7 @@ void help(const char *app, int more) {
     ("  Y   mirror ALL links located in the first level pages (mirror links)");
   infomsg("");
   infomsg("Proxy options:");
-  infomsg("  P  proxy use (-P [socks5://][user:pass@]proxy:port)");
+  infomsg("  P  proxy use (-P [socks5://|connect://][user:pass@]proxy:port)");
   infomsg(" %f *use proxy for ftp (f0 don't use)");
   infomsg(" %b  use this local hostname to make/send requests (-%b hostname)");
   infomsg("");
diff --git a/src/htslib.c b/src/htslib.c
index 90b358f4f..ddf5f6f42 100644
--- a/src/htslib.c
+++ b/src/htslib.c
@@ -947,9 +947,10 @@ int http_sendhead(httrackp * opt, t_cookie * cookie, int mode,
           /* widths bound method[256], url[HTS_URLMAXSIZE*2], protocol[256] */
           if (sscanf(line, "%255s %2047s %255s", method, url, protocol) == 3) {
             size_t ret;
-            // absolute-URI for an http proxy; a socks tunnel takes origin-form
+            // http proxy: absolute-URI; socks/CONNECT tunnel: origin-form
             if (retour->req.proxy.active &&
-                !hts_proxy_is_socks(retour->req.proxy.name)) {
+                !hts_proxy_is_socks(retour->req.proxy.name) &&
+                !hts_proxy_is_connect(retour->req.proxy.name)) {
               print_buffer(&bstr,
                       "%s http://%s%s %s\r\n", method, adr, url,
                       protocol);
@@ -985,9 +986,10 @@ int http_sendhead(httrackp * opt, t_cookie * cookie, int mode,
         print_buffer(&bstr, "HEAD ");
     }
 
-    // an http proxy needs an absolute URI; a socks tunnel does not
+    // an http proxy needs an absolute URI; a socks or CONNECT tunnel does not
     if (retour->req.proxy.active &&
         !hts_proxy_is_socks(retour->req.proxy.name) &&
+        !hts_proxy_is_connect(retour->req.proxy.name) &&
         (strncmp(adr, "https://", 8) != 0)) {
       if (!link_has_authority(adr)) {   // default http
 #if HDEBUG
@@ -1032,10 +1034,10 @@ int http_sendhead(httrackp * opt, t_cookie * cookie, int mode,
     if (xsend)
       print_buffer(&bstr, "%s", xsend);  // éventuelles autres lignes
 
-    // for https, auth rides the CONNECT (the tunneled GET would leak it); for
-    // socks, the handshake
+    // https/connect://: auth rides the CONNECT; socks: the handshake
     if (retour->req.proxy.active &&
         !hts_proxy_is_socks(retour->req.proxy.name) &&
+        !hts_proxy_is_connect(retour->req.proxy.name) &&
         strncmp(adr, "https://", 8) != 0) {
       if (link_has_authorization(retour->req.proxy.name)) {     // et hop, authentification proxy!
         const char *a = jump_identification_const(retour->req.proxy.name);
@@ -3717,6 +3719,8 @@ const char *jump_protocol_const(const char *source) {
     source += p;
   else if ((p = strfield(source, "socks5:")))
     source += p;
+  else if ((p = strfield(source, "connect:")))
+    source += p;
   // net_path
   if (strncmp(source, "//", 2) == 0)
     source += 2;
@@ -3732,6 +3736,12 @@ hts_boolean hts_proxy_is_socks(const char *name) {
                                                                    : HTS_FALSE;
 }
 
+hts_boolean hts_proxy_is_connect(const char *name) {
+  if (name == NULL)
+    return HTS_FALSE;
+  return strfield(name, "connect:") ? HTS_TRUE : HTS_FALSE;
+}
+
 // default proxy port for a -P argument, keyed on the scheme
 static int proxy_default_port(const char *arg) {
   return hts_proxy_is_socks(arg) ? 1080 : 8080;
diff --git a/src/htslib.h b/src/htslib.h
index 13d90dc15..b059dd757 100644
--- a/src/htslib.h
+++ b/src/htslib.h
@@ -210,6 +210,9 @@ int check_writeinput_t(T_SOC soc, int timeout);
 /* TRUE if this -P proxy name (which keeps its scheme) is a SOCKS5 proxy. */
 hts_boolean hts_proxy_is_socks(const char *name);
 
+/* TRUE if this -P proxy name is a "connect://" CONNECT-only proxy (#564). */
+hts_boolean hts_proxy_is_connect(const char *name);
+
 void treathead(t_cookie * cookie, const char *adr, const char *fil, htsblk * retour,
                char *rcvd);
 void treatfirstline(htsblk * retour, const char *rcvd);
diff --git a/src/htsproxy.c b/src/htsproxy.c
index 2ce5f8e21..e93f85d83 100644
--- a/src/htsproxy.c
+++ b/src/htsproxy.c
@@ -90,12 +90,15 @@ int http_proxy_tunnel(httrackp *opt, htsblk *retour, const char *adr,
   if (soc == INVALID_SOCKET)
     return 0;
 
-  // CONNECT needs an explicit host:port; default the https port
+  // CONNECT needs an explicit host:port; default :80 for http, :443 for https
   authority[0] = '\0';
   if (portsep != NULL)
     strlcatbuff(authority, host, sizeof(authority)); // already host:port
-  else
-    snprintf(authority, sizeof(authority), "%s:%d", host, 443);
+  else {
+    const int defport = (strncmp(adr, "https://", 8) == 0) ? 443 : 80;
+
+    snprintf(authority, sizeof(authority), "%s:%d", host, defport);
+  }
 
   // backstop: never let a stray CR/LF in the host smuggle a second line into
   // the CONNECT request (the host is already sanitized upstream)
@@ -132,7 +135,7 @@ int http_proxy_tunnel(httrackp *opt, htsblk *retour, const char *adr,
   }
   strlcatbuff(req, H_CRLF, sizeof(req)); // end of request headers
 
-  // raw send: ssl is set, so sendc() would route to TLS
+  // raw send(): sendc() would route to TLS when ssl is set (https tunnel)
   {
     const char *p = req;
     size_t remain = strlen(req);


Test patch:
diff --git a/src/htsselftest.c b/src/htsselftest.c
index 1e093d7be..1eb801ae1 100644
--- a/src/htsselftest.c
+++ b/src/htsselftest.c
@@ -1365,9 +1365,12 @@ static int st_proxyurl(httrackp *opt, int argc, char **argv) {
     return 1;
   }
   hts_parse_proxy(argv[0], name, sizeof(name), &port);
-  // host= is what the connect actually resolves (scheme + user:pass stripped)
-  printf("name=%s port=%d host=%s\n", name, port,
-         jump_identification_const(name));
+  // host= is the resolved host (scheme/userinfo stripped); kind= the transport
+  printf("name=%s port=%d host=%s kind=%s\n", name, port,
+         jump_identification_const(name),
+         hts_proxy_is_socks(name)     ? "socks"
+         : hts_proxy_is_connect(name) ? "connect"
+                                      : "http");
   return 0;
 }
 
diff --git a/tests/01_engine-proxyurl.test b/tests/01_engine-proxyurl.test
index e6cf76d0b..1bab54ac3 100755
--- a/tests/01_engine-proxyurl.test
+++ b/tests/01_engine-proxyurl.test
@@ -5,40 +5,51 @@ set -euo pipefail
 
 # hts_parse_proxy splits a -P argument "[scheme://][user:pass@]host[:port]" into
 # the proxy host string and port. -#test=proxyurl <arg> prints
-# "name=.. port=.. host=..", where host= is what the connect resolves.
+# "name=.. port=.. host=.. kind=..", where host= is what the connect resolves and
+# kind= is the transport the scheme selects (http, socks or connect).
 
 p() {
     test "$(httrack -O /dev/null -#test=proxyurl "$1")" == "$2" || exit 1
 }
 
 # bare host, with and without an explicit port
-p 'proxy.example.com:8080' 'name=proxy.example.com port=8080 host=proxy.example.com'
-p 'proxy.example.com' 'name=proxy.example.com port=8080 host=proxy.example.com'
+p 'proxy.example.com:8080' 'name=proxy.example.com port=8080 host=proxy.example.com kind=http'
+p 'proxy.example.com' 'name=proxy.example.com port=8080 host=proxy.example.com kind=http'
 
 # user:pass@ is kept in name (for proxy auth) and stripped from host
-p 'user:pass@proxy:3128' 'name=user:pass@proxy port=3128 host=proxy'
-p 'user:pass@proxy' 'name=user:pass@proxy port=8080 host=proxy'
+p 'user:pass@proxy:3128' 'name=user:pass@proxy port=3128 host=proxy kind=http'
+p 'user:pass@proxy' 'name=user:pass@proxy port=8080 host=proxy kind=http'
 
 # a scheme colon must not be read as a port: http://host with no port used to
 # parse to name=http with a garbage port
-p 'http://proxy:8080' 'name=http://proxy port=8080 host=proxy'
-p 'http://proxy' 'name=http://proxy port=8080 host=proxy'
+p 'http://proxy:8080' 'name=http://proxy port=8080 host=proxy kind=http'
+p 'http://proxy' 'name=http://proxy port=8080 host=proxy kind=http'
 
 # socks5/socks5h default to 1080 and resolve to the bare host
-p 'socks5://host:1080' 'name=socks5://host port=1080 host=host'
-p 'socks5://host' 'name=socks5://host port=1080 host=host'
-p 'socks5h://host' 'name=socks5h://host port=1080 host=host'
+p 'socks5://host:1080' 'name=socks5://host port=1080 host=host kind=socks'
+p 'socks5://host' 'name=socks5://host port=1080 host=host kind=socks'
+p 'socks5h://host' 'name=socks5h://host port=1080 host=host kind=socks'
 # explicit port wins over the 1080 default, with and without userinfo
-p 'socks5://host:9050' 'name=socks5://host port=9050 host=host'
-p 'socks5://user:pass@host:9050' 'name=socks5://user:pass@host port=9050 host=host'
+p 'socks5://host:9050' 'name=socks5://host port=9050 host=host kind=socks'
+p 'socks5://user:pass@host:9050' 'name=socks5://user:pass@host port=9050 host=host kind=socks'
+
+# connect:// is an http proxy driven with CONNECT for every request (#564); it
+# defaults to the http 8080 port and resolves to the bare host
+p 'connect://host' 'name=connect://host port=8080 host=host kind=connect'
+p 'connect://host:8082' 'name=connect://host port=8082 host=host kind=connect'
+p 'connect://user:pass@host:8082' 'name=connect://user:pass@host port=8082 host=host kind=connect'
+# only the scheme selects connect mode: a host literally named "connect" is a
+# plain http proxy, not a tunnel
+p 'connect:8080' 'name=connect port=8080 host=connect kind=http'
+p 'connect.example.com:3128' 'name=connect.example.com port=3128 host=connect.example.com kind=http'
 
 # the split is byte-transparent: percent-escapes stay encoded (decoded later at
 # auth time), only structural ASCII ':'/'@' delimit, and the last '@' ends the
 # userinfo. So %40/%3A and UTF-8 bytes never mis-split host or port.
-p 'socks5://user:p%40ss@host:1080' 'name=socks5://user:p%40ss@host port=1080 host=host'
-p 'socks5://us%3Aer:pass@host:1080' 'name=socks5://us%3Aer:pass@host port=1080 host=host'
-p 'socks5://a@b:c@host:1080' 'name=socks5://a@b:c@host port=1080 host=host'
-p 'socks5://naïve:pass@héllo:1080' 'name=socks5://naïve:pass@héllo port=1080 host=héllo'
+p 'socks5://user:p%40ss@host:1080' 'name=socks5://user:p%40ss@host port=1080 host=host kind=socks'
+p 'socks5://us%3Aer:pass@host:1080' 'name=socks5://us%3Aer:pass@host port=1080 host=host kind=socks'
+p 'socks5://a@b:c@host:1080' 'name=socks5://a@b:c@host port=1080 host=host kind=socks'
+p 'socks5://naïve:pass@héllo:1080' 'name=socks5://naïve:pass@héllo port=1080 host=héllo kind=socks'
 
 # a lone ':' must not underflow the backward scan
-p ':' 'name= port=8080 host='
+p ':' 'name= port=8080 host= kind=http'
diff --git a/tests/57_local-proxy-connect.test b/tests/57_local-proxy-connect.test
new file mode 100644
index 000000000..3564274e2
--- /dev/null
+++ b/tests/57_local-proxy-connect.test
@@ -0,0 +1,168 @@
+#!/bin/bash
+#
+# Issue #564: plain http through a CONNECT-only proxy (tor HTTPTunnelPort). With
+# a "connect://" proxy httrack must open a CONNECT tunnel to the origin and send
+# the request origin-form, like https already does, instead of the absolute-URI
+# form a CONNECT-only proxy rejects. Fully local: a plain-http origin behind a
+# proxy that answers CONNECT and 501s everything else.
+
+set -euo pipefail
+
+: "${top_srcdir:=..}"
+
+# shellcheck source=tests/testlib.sh
+. "$top_srcdir/tests/testlib.sh"
+
+python=$(find_python) || python=
+if test -z "$python"; then
+    echo "python3 missing, skipping"
+    exit 77
+fi
+
+server=$(nativepath "$top_srcdir/tests/proxy-connect-server.py")
+tmpdir=$(mktemp -d)
+pids=
+
+cleanup() {
+    for pid in $pids; do
+        stop_server "$pid"
+    done
+    rm -rf "$tmpdir"
+}
+trap cleanup EXIT
+
+# start_server <logdir> <mode>: launches a proxy+origin pair, sets $origin_port
+# and $proxy_port from its announced ephemeral ports.
+start_server() {
+    local dir="$1" mode="$2" ports
+    mkdir -p "$dir"
+    ports="$dir/ports.txt"
+    dir_native=$(nativepath "$dir")
+    : >"$ports"
+    "$python" "$server" "$dir_native" "$mode" \
+        >"$ports" 2>"$dir/server.err" &
+    pids="$pids $!"
+    for _ in $(seq 1 100); do
+        grep -q "^ready" "$ports" 2>/dev/null && break
+        sleep 0.1
+    done
+    grep -q "^ready" "$ports" 2>/dev/null || {
+        echo "server ($mode) did not start" >&2
+        cat "$dir/server.err" >&2
+        exit 1
+    }
+    origin_port=$(awk '/^ORIGIN/{print $2}' "$ports")
+    proxy_port=$(awk '/^PROXY/{print $2}' "$ports")
+}
+
+# Kill httrack after a deadline so a hang surfaces as $HANG_RC, not a stalled job
+# (a portable stand-in for `timeout`, which macOS lacks).
+HANG_RC=137 # 128 + SIGKILL
+run_crawl() {
+    local out="$1" proxy="$2" url="${4:-http://127.0.0.1:${3}/}"
+    rm -rf "$out"
+    httrack "$url" --proxy "$proxy" \
+        -O "$out" -r1 -s0 --timeout=10 >"$out.log" 2>&1 &
+    local pid=$!
+    (sleep 60 && kill -9 "$pid" 2>/dev/null) &
+    local guard=$!
+    local rc=0
+    wait "$pid" 2>/dev/null || rc=$?
+    kill "$guard" 2>/dev/null || true
+    wait "$guard" 2>/dev/null || true
+    return "$rc"
+}
+
+# --- working proxy ----------------------------------------------------------
+ok="$tmpdir/ok"
+start_server "$ok" ok
+
+# 1. page fetched through a CONNECT tunnel, request delivered origin-form
+run_crawl "$ok/out" "connect://127.0.0.1:${proxy_port}" "$origin_port"
+grep -rq "ORIGIN-PAGE-564" "$ok/out" || {
+    echo "FAIL: origin page not downloaded through the CONNECT tunnel" >&2
+    cat "$ok/out.log" >&2
+    exit 1
+}
+grep -q "^CONNECT 127.0.0.1:${origin_port} " "$ok/proxy.log" || {
+    echo "FAIL: proxy never received a CONNECT for the plain-http origin" >&2
+    cat "$ok/proxy.log" >&2
+    exit 1
+}
+grep -q "^GET / HTTP/1" "$ok/origin-headers.log" || {
+    echo "FAIL: origin did not see an origin-form request line" >&2
+    cat "$ok/origin-headers.log" >&2
+    exit 1
+}
+if grep -q "^GET http://" "$ok/origin-headers.log"; then
+    echo "FAIL: absolute-URI request leaked through the tunnel to the origin" >&2
+    exit 1
+fi
+echo "OK: plain http tunneled origin-form through CONNECT"
+
+# 2. default CONNECT port follows the origin scheme: a portless http origin
+# tunnels to :80 (not the https :443). Nothing serves :80 here so the fetch
+# fails; only the CONNECT target port is asserted. Reverting to a hardcoded 443
+# makes this log ":443" and fail.
+: >"$ok/proxy.log"
+run_crawl "$ok/out_port" "connect://127.0.0.1:${proxy_port}" "" "http://127.0.0.1/" || true
+grep -q "^CONNECT 127.0.0.1:80 " "$ok/proxy.log" || {
+    echo "FAIL: portless http origin did not CONNECT to the default port 80" >&2
+    cat "$ok/proxy.log" >&2
+    exit 1
+}
+echo "OK: portless http origin tunnels to the default port 80"
+
+# 3. teeth: without connect://, httrack sends the absolute-URI form, which the
+# CONNECT-only proxy rejects (501) -> nothing fetched. Proves the scheme is what
+# flips the behavior and that the proxy really is CONNECT-only.
+: >"$ok/proxy.log"
+: >"$ok/origin-headers.log"
+run_crawl "$ok/out_plain" "127.0.0.1:${proxy_port}" "$origin_port" || true
+grep -rq "ORIGIN-PAGE-564" "$ok/out_plain" && {
+    echo "FAIL: absolute-URI form unexpectedly fetched through a CONNECT-only proxy" >&2
+    exit 1
+}
+grep -q "^GET http://127.0.0.1:${origin_port}/" "$ok/proxy.log" || {
+    echo "FAIL: a plain proxy should have received an absolute-URI GET" >&2
+    cat "$ok/proxy.log" >&2
+    exit 1
+}
+echo "OK: absolute-URI form (no connect://) rejected by the CONNECT-only proxy"
+
+# 4. authenticated proxy: creds ride the CONNECT, never reach the origin
+: >"$ok/proxy.log"
+: >"$ok/origin-headers.log"
+run_crawl "$ok/out2" "connect://user:secret@127.0.0.1:${proxy_port}" "$origin_port"
+grep -rq "ORIGIN-PAGE-564" "$ok/out2" || {
+    echo "FAIL: origin page not downloaded through the authenticated proxy" >&2
+    exit 1
+}
+got=$(awk '/^AUTH Basic /{print $3}' "$ok/proxy.log" | head -1)
+# base64("user:secret"); compared literally to stay portable (no base64 -d)
+test "$got" == "dXNlcjpzZWNyZXQ=" || {
+    echo "FAIL: Proxy-Authorization not carried on CONNECT (got '$got')" >&2
+    cat "$ok/proxy.log" >&2
+    exit 1
+}
+if grep -qi "proxy-authorization" "$ok/origin-headers.log"; then
+    echo "FAIL: proxy credentials leaked to the origin through the tunnel" >&2
+    exit 1
+fi
+echo "OK: proxy credentials carried on CONNECT, not leaked to origin"
+
+# --- hostile proxy ----------------------------------------------------------
+# A proxy that answers 200 then streams headers forever must not hang the crawl.
+flood="$tmpdir/flood"
+start_server "$flood" flood
+rc=0
+run_crawl "$flood/out" "connect://127.0.0.1:${proxy_port}" "$origin_port" || rc=$?
+test "$rc" -ne "$HANG_RC" || {
+    echo "FAIL: crawl hung on a flooding proxy (bounded read missing)" >&2
+    exit 1
+}
+grep -rq "ORIGIN-PAGE-564" "$flood/out" 2>/dev/null && {
+    echo "FAIL: flooding proxy unexpectedly served the page" >&2
+    exit 1
+}
+echo "OK: bounded proxy response, no hang on a flooding proxy"
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 090716dd4..b1ac82997 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -2,7 +2,7 @@
 # explicitly: automake does not expand wildcards in EXTRA_DIST, so a glob would
 # silently drop it from the dist tarball and break "make distcheck".
 EXTRA_DIST = $(TESTS) crawl-test.sh run-all-tests.sh check-network.sh \
-	proxy-https-server.py socks5-server.py \
+	proxy-https-server.py socks5-server.py proxy-connect-server.py \
 	local-crawl.sh local-server.py testlib.sh server.crt server.key \
 	server-root/simple/basic.html server-root/simple/link.html \
 	server-root/stripquery/index.html server-root/stripquery/a.html \
@@ -133,6 +133,7 @@ TESTS = \
 	53_local-proxytrack-cache-corrupt.test \
 	54_local-update-truncate-purge.test \
 	55_local-chunked.test \
-	56_local-proxy-noleak.test
+	56_local-proxy-noleak.test \
+	57_local-proxy-connect.test
 
 CLEANFILES = check-network_sh.cache
diff --git a/tests/proxy-connect-server.py b/tests/proxy-connect-server.py
new file mode 100644
index 000000000..95836338c
--- /dev/null
+++ b/tests/proxy-connect-server.py
@@ -0,0 +1,152 @@
+#!/usr/bin/env python3
+"""Local CONNECT-only proxy + plain-HTTP origin for the issue #564 test.
+
+Models a tor HTTPTunnelPort: an HTTP proxy that honours CONNECT and rejects
+every other method with 501 (the classic absolute-URI form a normal HTTP proxy
+accepts). Fronting it is a plain-HTTP origin. The proxy logs each request line
+(and any Proxy-Authorization); the origin logs its request line and headers.
+That lets the test assert a plain-http crawl tunneled through CONNECT, arrived
+origin-form (not "GET http://host/..."), and never leaked proxy credentials.
+
+Proxy modes (argv[2], default "ok"):
+  ok    - honour CONNECT and tunnel to the origin
+  flood - answer 200 then stream headers forever with no blank line, to exercise
+          the client's bound on the proxy response (must not hang the crawl)
+
+Usage: proxy-connect-server.py <logdir> [mode]
+Prints "ORIGIN <port>", "PROXY <port>", then "ready" (one per line) on stdout.
+"""
+import http.server
+import os
+import socket
+import socketserver
+import sys
+import threading
+
+ORIGIN_BODY = b"<html><body>ORIGIN-PAGE-564</body></html>"
+PROXY_LOG = "proxy.log"
+ORIGIN_LOG = "origin-headers.log"
+
+
+def make_origin(logdir):
+    class Origin(http.server.BaseHTTPRequestHandler):
+        def do_GET(self):
+            with open(os.path.join(logdir, ORIGIN_LOG), "a") as handle:
+                handle.write(self.requestline + "\n")
+                for key in self.headers.keys():
+                    handle.write(key + "\n")
+            self.send_response(200)
+            self.send_header("Content-Type", "text/html")
+            self.send_header("Content-Length", str(len(ORIGIN_BODY)))
+            self.end_headers()
+            self.wfile.write(ORIGIN_BODY)
+
+        def log_message(self, *args):
+            pass
+
+    return Origin
+
+
+def start_origin(logdir):
+    httpd = socketserver.TCPServer(("127.0.0.1", 0), make_origin(logdir))
+    port = httpd.socket.getsockname()[1]
+    threading.Thread(target=httpd.serve_forever, daemon=True).start()
+    return port
+
+
+def pipe(src, dst):
+    try:
+        while True:
+            data = src.recv(65536)
+            if not data:
+                break
+            dst.sendall(data)
+    except OSError:
+        pass
+    finally:
+        for sock in (src, dst):
+            try:
+                sock.shutdown(socket.SHUT_RDWR)
+            except OSError:
+                pass
+
+
+def handle_client(conn, logdir, mode):
+    rfile = conn.makefile("rb")
+    request_line = rfile.readline().decode("latin-1").strip()
+    auth = None
+    while True:
+        line = rfile.readline().decode("latin-1")
+        if line in ("\r\n", "\n", ""):
+            break
+        key, _, value = line.partition(":")
+        if key.strip().lower() == "proxy-authorization":
+            auth = value.strip()
+    with open(os.path.join(logdir, PROXY_LOG), "a") as handle:
+        handle.write(request_line + "\n")
+        if auth is not None:
+            handle.write("AUTH " + auth + "\n")
+    parts = request_line.split()
+    # CONNECT-only: reject the classic absolute-URI form a normal proxy accepts
+    if not (len(parts) >= 2 and parts[0] == "CONNECT"):
+        conn.sendall(b"HTTP/1.0 501 Not Implemented\r\n\r\n")
+        conn.close()
+        return
+    if mode == "flood":
+        # 200, then an endless header stream with no terminating blank line: the
+        # client must bound this and give up, not hang.
+        try:
+            conn.sendall(b"HTTP/1.0 200 Connection established\r\n")
+            while True:
+                conn.sendall(b"X-Pad: 0123456789\r\n")
+        except OSError:
+            pass
+        conn.close()
+        return
+    host, _, port = parts[1].partition(":")
+    try:
+        upstream = socket.create_connection((host, int(port or 80)))
+    except OSError:
+        conn.sendall(b"HTTP/1.0 502 Bad Gateway\r\n\r\n")
+        conn.close()
+        return
+    conn.sendall(b"HTTP/1.0 200 Connection established\r\n\r\n")
+    threading.Thread(target=pipe, args=(conn, upstream), daemon=True).start()
+    pipe(upstream, conn)
+
+
+def start_proxy(logdir, mode):
+    srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+    srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+    srv.bind(("127.0.0.1", 0))
+    srv.listen(16)
+    port = srv.getsockname()[1]
+
+    def serve():
+        while True:
+            conn, _ = srv.accept()
+            threading.Thread(
+                target=handle_client, args=(conn, logdir, mode), daemon=True
+            ).start()
+
+    threading.Thread(target=serve, daemon=True).start()
+    return port
+
+
+def main():
+    logdir = sys.argv[1]
+    mode = sys.argv[2] if len(sys.argv) > 2 else "ok"
+    for name in (PROXY_LOG, ORIGIN_LOG):
+        open(os.path.join(logdir, name), "w").close()
+    origin_port = start_origin(logdir)
+    proxy_port = start_proxy(logdir, mode)
+    # Keep the port lines the caller parses LF: Windows would emit \r\n.
+    sys.stdout.reconfigure(newline="\n")
+    print("ORIGIN %d" % origin_port, flush=True)
+    print("PROXY %d" % proxy_port, flush=True)
+    print("ready", flush=True)
+    threading.Event().wait()
+
+
+if __name__ == "__main__":
+    main()