You are confusing forward and reverse proxies.
Conceptually it works like this:
Reverse proxies
- are deployed near the server side of a connection
- pretend to be the origin
- are controlled by or on behalf of the site owner
- are not inherently known to the user agent
Forward proxies
- are deployed near the client side of a connection
- are controlled by or on behalf of the user agent
- are explicitely configured in the user agent
(Reality is more complicated than that, of course, but this is sufficient to highlight the differences).
Internet || Invisible to
+ || User Agent
| ||
+------------+ +---------------+ | +---------------+ || +--------+
| | | | | | | || | |
| User Agent +---->+ Forward Proxy +-------->+ Reverse Proxy +----->+ Origin |
| | | | | | | || | |
+------------+ +---------------+ | +---------------+ || +--------+
| ||
+ ||
||
nginx is a reverse proxy, but by setting the Transport.Proxy field you treat it like a forward proxy. This is the request that nginx sees:
CONNECT example.com:443 HTTP/1.1
Host: example.com:443
User-Agent: Go-http-client/1.1
This essentialy means, "Establish a TCP connection to example.com:443 and then act like a dumb TCP proxy." Since nginx is a reverse proxy only it is rightfully confused when confronted with a CONNECT request.
To send a request to a particular reverse proxy you simply have to modify the request URL, and possibly the Host header (that depends on whether or not nginx expects a particular server_name
). No special client configuration is required.
Assuming nginx runs on 198.51.100.1
:
req, _ := http.NewRequest("GET", "http://198.51.100.1", nil)
req.Host = "example.com" // if necessary
res, _ := http.DefaultClient.Do(req)
This causes the following request to be sent to 198.51.100.1:80:
GET / HTTP/1.1
Host: example.com
User-Agent: Go-http-client/1.1
Accept-Encoding: gzip
Note that it is entirely up to the reverse proxy if the request actually hits example.com. The client has no knowledge or control over what happens after the proxy.
If you are not in a position to change the request, you can set the Transport.DialContext function such that your proxy is always dialed, independent of the request URL and Host header. This results in the same request as above and should be equivalent to your JavaScript code:
c := &http.Client{
Transport: &http.Transport{
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
return (&net.Dialer{}).DialContext(ctx, "tcp", "198.51.100.1:80")
// Or 198.51.100.1:443 if nginx has TLS enabled, although that almost
// certainly causes TLS validation errors because a certificate for
// example.com is expected.
},
},
}
req, _ := http.NewRequest("GET", "http://example.com", nil)
res, _ := c.Do(req)