ChatGPT解决这个技术问题 Extra ChatGPT

How can I use an http proxy with node.js http.Client?

I want to make an outgoing HTTP call from node.js, using the standard http.Client. But I cannot reach the remote server directly from my network and need to go through a proxy.

How do I tell node.js to use the proxy?

I'm having the same issue. Node.js is behind a firewall and I am unable to create an HTTPClient to an external website.

C
Community

Tim Macfarlane's answer was close with regards to using a HTTP proxy.

Using a HTTP proxy (for non secure requests) is very simple. You connect to the proxy and make the request normally except that the path part includes the full url and the host header is set to the host you want to connect to. Tim was very close with his answer but he missed setting the host header properly.

var http = require("http");

var options = {
  host: "proxy",
  port: 8080,
  path: "http://www.google.com",
  headers: {
    Host: "www.google.com"
  }
};
http.get(options, function(res) {
  console.log(res);
  res.pipe(process.stdout);
});

For the record his answer does work with http://nodejs.org/ but that's because their server doesn't care the host header is incorrect.


Is there a way to use http proxy connect https port? seems has no easy method
@Gohan See Chris's answer below for an example on how to connect to an https server through and http proxy.
if you get bad request, put path: '/'
How can I integrate proxy-user and proxy-password in the options block?
Has this changed? Even with the final destination as another local server, I get a 404, and the destination server never receives the request..
S
Shane Bishop

EDIT: As of Feb 11th 2020, request is fully deprecated. No new changes are expected.

You can use request, I just found it's unbelievably easy to use proxy on node.js, just with one external "proxy" parameter, even more it supports HTTPS through a http proxy.

var request = require('request');

request({
  'url':'https://anysite.you.want/sub/sub',
  'method': "GET",
  'proxy':'http://yourproxy:8087'
},function (error, response, body) {
  if (!error && response.statusCode == 200) {
    console.log(body);
  }
})

Worked for both http and https in my case, Thanks a lot
any ideas why this won't work for internal corp pages?
I'm surprised internal corp pages are behind a proxy. Are you sure that the proxy is not being bypassed for internal pages? Is it on a different vlan?
U need to specify authentication somehow (gonna post it here if I figure it out)
@IgorL. I got it to work with Auth and the Request Module, with headers: {'Proxy-Authorization': XXXX}
C
Chris

One thing that took me a while to figure out, use 'http' to access the proxy, even if you're trying to proxy through to a https server. This works for me using Charles (osx protocol analyser):

var http = require('http');

http.get ({
    host: '127.0.0.1',
    port: 8888,
    path: 'https://www.google.com/accounts/OAuthGetRequestToken'
}, function (response) {
    console.log (response);
});

Above code is not working for me, and its related to issue github.com/joyent/node/issues/2474 check koichik's answer we have to use "method":"connect" and on "connect" event, we have send path information.
A
Alexey Volodko

I bought private proxy server, after purchase I got:

255.255.255.255 // IP address of proxy server
99999 // port of proxy server
username // authentication username of proxy server
password // authentication password of proxy server

And I wanted to use it. First answer and second answer worked only for http(proxy) -> http(destination), however I wanted http(proxy) -> https(destination).

And for https destination it would be better to use HTTP tunnel directly. I found solution here.

Node v8:

const http = require('http')
const https = require('https')
const username = 'username'
const password = 'password'
const auth = 'Basic ' + Buffer.from(username + ':' + password).toString('base64')

http.request({
  host: '255.255.255.255', // IP address of proxy server
  port: 99999, // port of proxy server
  method: 'CONNECT',
  path: 'kinopoisk.ru:443', // some destination, add 443 port for https!
  headers: {
    'Proxy-Authorization': auth
  },
}).on('connect', (res, socket) => {
  if (res.statusCode === 200) { // connected to proxy server
    https.get({
      host: 'www.kinopoisk.ru',
      socket: socket,    // using a tunnel
      agent: false,      // cannot use a default agent
      path: '/your/url'  // specify path to get from server
    }, (res) => {
      let chunks = []
      res.on('data', chunk => chunks.push(chunk))
      res.on('end', () => {
        console.log('DONE', Buffer.concat(chunks).toString('utf8'))
      })
    })
  }
}).on('error', (err) => {
  console.error('error', err)
}).end()

Node v14:

const http = require('http');
const https = require('https');
const username = 'username';
const password = 'password';
const auth = 'Basic ' + Buffer.from(username + ':' + password).toString('base64');

http.request({
  host: '255.255.255.255', // IP address of proxy server
  port: 99999, // port of proxy server
  method: 'CONNECT',
  path: 'kinopoisk.ru:443', // some destination, add 443 port for https!
  headers: {
    'Proxy-Authorization': auth
  },
}).on('connect', (res, socket) => {
  if (res.statusCode === 200) { // connected to proxy server
    const agent = new https.Agent({ socket });
    https.get({
      host: 'www.kinopoisk.ru',
      path: '/',
      agent,      // cannot use a default agent
    }, (res) => {
      let chunks = []
      res.on('data', chunk => chunks.push(chunk))
      res.on('end', () => {
        console.log('DONE', Buffer.concat(chunks).toString('utf8'))
      })
    })
  }
}).on('error', (err) => {
  console.error('error', err)
}).end();

The socket property isn't documented in nodejs. Was this option removed.
The described approach won't work nowadays. socket option is not supported in http modules' get / request functions and simply gets ignored.
So what now? What should we use now?
T
Tim Macfarlane

As @Renat here already mentioned, proxied HTTP traffic comes in pretty normal HTTP requests. Make the request against the proxy, passing the full URL of the destination as the path.

var http = require ('http');

http.get ({
    host: 'my.proxy.com',
    port: 8080,
    path: 'http://nodejs.org/'
}, function (response) {
    console.log (response);
});

This seems to work though Fiddler calls it a protocol violation which suggests it's not a proper HTTP request-via-proxy...
m
major-mann

Thought I would add this module I found: https://www.npmjs.org/package/global-tunnel, which worked great for me (Worked immediately with all my code and third party modules with only the code below).

require('global-tunnel').initialize({
  host: '10.0.0.10',
  port: 8080
});

Do this once, and all http (and https) in your application goes through the proxy.

Alternately, calling

require('global-tunnel').initialize();

Will use the http_proxy environment variable


This worked for me! Actually this way you decouple the proxy from the code and use the existing configuration for npm! that is the way to go I would say
@NeelBasu Yes it does
As soon as I use this, the applications requests don't work anymore: sign in failed with error RequestError: Protocol "https:" not supported. Expected "undefined"
s
startswithaj

The 'request' http package seems to have this feature:

https://github.com/mikeal/request

For example, the 'r' request object below uses localproxy to access its requests:

var r = request.defaults({'proxy':'http://localproxy.com'})

http.createServer(function (req, resp) {
  if (req.url === '/doodle.png') {
    r.get('http://google.com/doodle.png').pipe(resp)
  }
})

Unfortunately there are no "global" defaults so that users of libs that use this cannot amend the proxy unless the lib pass through http options...

HTH, Chris


the request http package makes it easier to allow your code to toggle between proxy and non proxy usage (which is quite useful on my laptop).
V
Vyacheslav Voronchuk

In case you need to the use basic authorisation for your proxy provider, just use the following:

var http = require("http");

var options = {
    host:       FarmerAdapter.PROXY_HOST,
    port:       FarmerAdapter.PROXY_PORT,
    path:       requestedUrl,
    headers:    {
        'Proxy-Authorization':  'Basic ' + new Buffer(FarmerAdapter.PROXY_USER + ':' + FarmerAdapter.PROXY_PASS).toString('base64')
    }
};

var request = http.request(options, function(response) {
    var chunks = [];
    response.on('data', function(chunk) {
        chunks.push(chunk);
    });
    response.on('end', function() {
        console.log('Response', Buffer.concat(chunks).toString());
    });
});

request.on('error', function(error) {
    console.log(error.message);
});

request.end();

where can i find "FarmerAdapter"?
R
Renat

Basically you don't need an explicit proxy support. Proxy protocol is pretty simple and based on the normal HTTP protocol. You just need to use your proxy host and port when connecting with HTTPClient. Example (from node.js docs):

var http = require('http');
var google = http.createClient(3128, 'your.proxy.host');
var request = google.request('GET', '/',
  {'host': 'www.google.com'});
request.end();
...

So basically you connect to your proxy but do a request to "http://www.google.com".


http.createClient is deprecated, Tim Macfarlane is using the newer http.get below
This will apparently no longer work with node.js as of v5.6 as they have removed createClient.
L
Luke

Node should support using the http_proxy environmental variable - so it is cross platform and works on system settings rather than requiring a per-application configuration.

Using the provided solutions, I would recommend the following:

Coffeescript

get_url = (url, response) ->
  if process.env.http_proxy?
    match = process.env.http_proxy.match /^(http:\/\/)?([^:\/]+)(:([0-9]+))?/i
    if match
      http.get { host: match[2], port: (if match[4]? then match[4] else 80), path: url }, response
      return
  http.get url, response

Javascript

get_url = function(url, response) {
  var match;
  if (process.env.http_proxy != null) {
    match = process.env.http_proxy.match(/^(http:\/\/)?([^:\/]+)(:([0-9]+))?/i);
    if (match) {
      http.get({
        host: match[2],
        port: (match[4] != null ? match[4] : 80),
        path: url
      }, response);
      return;
    }
  }
  return http.get(url, response);
};

Usage To use the method, effectively just replace http.get, for instance the following writes the index page of google to a file called test.htm:

file = fs.createWriteStream path.resolve(__dirname, "test.htm")
get_url "http://www.google.com.au/", (response) ->
  response.pipe file
  response.on "end", ->
    console.log "complete"

Setting http_proxy doesn't appear to have any effect when running Node on Windows.
It should work under Windows (that is the primary system I'm using). Make sure after you have set the setting that you have reset your terminal session (if set through control panel and not set). You should be able to check it's set correctly using echo %HTTP_PROXY% Or even better you should use node itself node -e "console.log(process.env.http_proxy);" This worked for me under Windows, so good luck.
r
ring bearer

I think there a better alternative to the answers as of 2019. We can use the global-tunnel-ng package to initialize proxy and not pollute the http or https based code everywhere. So first install global-tunnel-ng package:

npm install global-tunnel-ng

Then change your implementations to initialize proxy if needed as:

const globalTunnel = require('global-tunnel-ng');

globalTunnel.initialize({
  host: 'proxy.host.name.or.ip',
  port: 8080
});

V
Vasily Kushakov

Imskull's answer almost worked for me, but I had to make some changes. The only real change is adding username, password, and setting rejectUnauthorized to false. I couldn't comment so I put this in an answer.

If you run the code it'll get you the titles of the current stories on Hacker News, per this tutorial: http://smalljs.org/package-managers/npm/

var cheerio = require('cheerio');
var request = require('request');

request({
    'url': 'https://news.ycombinator.com/',
    'proxy': 'http://Username:Password@YourProxy:Port/',
    'rejectUnauthorized': false
}, function(error, response, body) {
    if (!error && response.statusCode == 200) {
        if (response.body) {
            var $ = cheerio.load(response.body);
            $('td.title a').each(function() {
                console.log($(this).text());
            });
       }
    } else {
        console.log('Error or status not equal 200.');
    }
});

f
fullstacklife

May not be the exact one-liner you were hoping for but you could have a look at http://github.com/nodejitsu/node-http-proxy as that may shed some light on how you can use your app with http.Client.


How is this helpful?
d
ddallala

http://groups.google.com/group/nodejs/browse_thread/thread/d5aadbcaa00c3f7/12ebf01d7ec415c3?lnk=gst&q=proxy#12ebf01d7ec415c3

Based on the answers from this thread it would seem like you could use proxychains to run node.js through the proxy server:
$ proxychains /path/to/node application.js

Personally I wasnt able to install any of the proxychains versions on Cygwin/Windows environment so couldn't test it.

Furthermore, they also talked about using connect-proxy but I could not find any documentation on how to do this.

In short, I'm still stuck, but maybe someone can use this info to find a suitable work-around.


update: after some investigating found out that I could not build proxychains on CygWin because RTLD_NEXT is not supported.
B
Bad Green

use 'https-proxy-agent' like this

var HttpsProxyAgent = require('https-proxy-agent');
var proxy = process.env.https_proxy || 'other proxy address';
var agent = new HttpsProxyAgent(proxy);

options = {
    //...
    agent : agent
}

https.get(options, (res)=>{...});

E
Emeeus

If you have the Basic http authentication scheme you have to make a base64 string of myuser:mypassword, and then add "Basic" in the beginning. That's the value of Proxy-Authorization header, here an example:

var Http = require('http');

var req = Http.request({
    host: 'myproxy.com.zx',
    port: 8080,
    headers:{"Proxy-Authorization": "Basic bXl1c2VyOm15cGFzc3dvcmQ="},
    method: 'GET',
    path: 'http://www.google.com/'
    }, function (res) {
        res.on('data', function (data) {
        console.log(data.toString());
    });
});

req.end();

In nodejs you could use Buffer to encode

var encodedData = Buffer.from('myuser:mypassword').toString('base64');

console.log(encodedData);

Just as example, in browsers you could encode in base64 using btoa(), useful in ajax requests in a browser without proxy settings performing a request using proxy.

var encodedData = btoa('myuser:mypassword') console.log(encodedData);

How to find wich scheme accepts the proxy server?

If we don't have a custom DNS configured (that would throw something like ERR_NAME_NOT_RESOLVED), when we perform a request, the response (code 407) should inform in the response headers which http authentication scheme the proxy is using.