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

19

167

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?

Centric answered 5/10, 2010 at 10:32 Comment(2)
I'm having the same issue. Node.js is behind a firewall and I am unable to create an HTTPClient to an external website.Petrina
You can use axios for this purpose. Check my answer hereSiracusa
F
173

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.

Furnishing answered 21/7, 2011 at 19:26 Comment(10)
Is there a way to use http proxy connect https port? seems has no easy methodSvensen
@Svensen See Chris's answer below for an example on how to connect to an https server through and http proxy.Sophi
if you get bad request, put path: '/'Empale
How can I integrate proxy-user and proxy-password in the options block?Exert
Has this changed? Even with the final destination as another local server, I get a 404, and the destination server never receives the request..Milburr
How can this be achieved on a global level? I'am able to do this on single requests but I have vendor modules which I also need to use the proxy connection.Hyperbolism
I am getting this error with above code Error: getaddrinfo ENOTFOUND proxy proxy:8080Extortion
Even without using a proxy, this doesn't work. I don't think the "path" field can be used this way, I get a connection refused error with options = { path: "http://www.example.com/", method: "GET", headers: {Host: "www.example.com"}}.Chelate
@Furnishing hi can you look my question #53593682 ? I can't make your solution work.Copra
If I'm not familiar with the Node.js framework, could you please tell me where exactly to put this snippet? What if my application is running through sails.js ? ThanksVanquish
C
71

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);
  }
})
Cruel answered 22/1, 2014 at 11:1 Comment(7)
Worked for both http and https in my case, Thanks a lotOkoka
any ideas why this won't work for internal corp pages?Protozoal
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?Ovotestis
U need to specify authentication somehow (gonna post it here if I figure it out)Topotype
I got this error using request with proxy: Error: tunneling socket could not be established, cause=connect ECONNREFUSED 127.0.0.1:80Skater
@IgorL. I got it to work with Auth and the Request Module, with headers: {'Proxy-Authorization': XXXX}Sascha
If the proxy is not HTTPS, isn't this insecure (password in cleartext)?Blas
C
40

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);
});
Crane answered 16/6, 2011 at 2:53 Comment(1)
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.Tenno
A
31

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();
Arriaga answered 2/4, 2018 at 12:57 Comment(7)
The socket property isn't documented in nodejs. Was this option removed.Turves
The described approach won't work nowadays. socket option is not supported in http modules' get / request functions and simply gets ignored.Diplomatics
So what now? What should we use now?Pyosis
@MelroyvandenBerg The answer was updated to use an Agent with the socket specified.Pyroconductivity
The socket property only works in HTTPS requests, because you can specify a socket when create a tls socket. See the tls.connect() documentation.Lashaun
I receive 502 status in "on connect" callback. So the solution doesn't work for mePeculium
I had to change the 'Proxy-Authorization' header to lowercase ('proxy-authorization')Pantomimist
F
16

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);
});
Foldboat answered 5/10, 2010 at 10:33 Comment(1)
This seems to work though Fiddler calls it a protocol violation which suggests it's not a proper HTTP request-via-proxy...Ankle
P
13

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

Plashy answered 4/8, 2014 at 15:58 Comment(3)
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 sayMcclean
@NeelBasu Yes it doesPlashy
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"Spradlin
C
8

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

Caper answered 14/10, 2012 at 13:58 Comment(1)
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).Hulk
C
6

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();
Capitulary answered 19/1, 2016 at 16:28 Comment(1)
where can i find "FarmerAdapter"?Thither
A
5

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".

Atchison answered 4/11, 2010 at 16:6 Comment(2)
http.createClient is deprecated, Tim Macfarlane is using the newer http.get belowFreely
This will apparently no longer work with node.js as of v5.6 as they have removed createClient.Ankle
S
4

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"
Sheared answered 13/8, 2013 at 0:32 Comment(2)
Setting http_proxy doesn't appear to have any effect when running Node on Windows.Howlet
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.Sheared
S
3

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
});
Snubnosed answered 4/3, 2019 at 5:55 Comment(0)
S
2

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)=>{...});
Stodge answered 28/11, 2017 at 16:11 Comment(0)
R
2

Just run nodejs with proxy wrapper like tsocks tsocks node myscript.js

Original solution: Doing http requests through a SOCKS5 proxy in NodeJS

More info: https://www.binarytides.com/proxify-applications-with-tsocks-and-proxychains-on-ubuntu/

For windows: https://superuser.com/questions/319516/how-to-force-any-program-to-use-socks

Reinwald answered 30/9, 2020 at 20:31 Comment(0)
N
1

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.

Naranjo answered 10/10, 2010 at 22:23 Comment(1)
How is this helpful?Hypnos
P
1

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.

Petrina answered 28/12, 2010 at 21:15 Comment(1)
update: after some investigating found out that I could not build proxychains on CygWin because RTLD_NEXT is not supported.Petrina
L
1

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.');
    }
});
Luthern answered 25/5, 2016 at 21:2 Comment(0)
S
1

Only set the environment variable and the http will use proxy.

const env = {
    "host": "proxy.server.com.br",
    "port": 8080,
    "user": "my-user",
    "pass": "my-pass"
};

process.env.http_proxy =
  process.env.https_proxy =
    `http://${env.user}:${env.pass}@${env.host}:${env.port}/`;
Seventy answered 20/8, 2022 at 4:55 Comment(0)
S
1

Axios has proxy option in its documentation. Also you can define http_proxy and https_proxy environment variables

// `proxy` defines the hostname, port, and protocol of the proxy server.
// You can also define your proxy using the conventional `http_proxy` and
// `https_proxy` environment variables. If you are using environment variables
// for your proxy configuration, you can also define a `no_proxy` environment
// variable as a comma-separated list of domains that should not be proxied.
// Use `false` to disable proxies, ignoring environment variables.
// `auth` indicates that HTTP Basic auth should be used to connect to the proxy, and
// supplies credentials.
// This will set an `Proxy-Authorization` header, overwriting any existing
// `Proxy-Authorization` custom headers you have set using `headers`.
// If the proxy server uses HTTPS, then you must set the protocol to `https`. 

proxy: {
  protocol: 'https',
  host: '127.0.0.1',
  port: 9000,
  auth: {
    username: 'mikeymike',
    password: 'rapunz3l'
  }
},
Siracusa answered 21/10, 2022 at 22:55 Comment(0)
R
0

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.

Regen answered 3/5, 2018 at 18:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.