axios: HTTPS over HTTP Proxy Fails

Describe the bug

HTTPS over HTTP Proxy Fails with 500 handshakefailed on mcafee proxy. mcafee initialize server context:handshakefailed:server state 1:state 9:Application response 500 handshakefailed With HTTP_PROXY and HTTPS_PROXY set, curl works but axios.get fails.

To Reproduce

import axios from 'axios';

(async () => {
  try {
    console.log(await axios.get('https://github.com/'));
  } catch (e) {
    console.log(e);
  }
})();

Expected behavior

Request should succeed.

Environment

  • Axios Version [0.21.0]
  • Node.js Version [v12.18.3]
  • OS: [OSX 10.15.7]

Additional context/Screenshots

N/A

Current workaround

Install global-agent, remove the HTTP_PROXY env vars and set GLOBAL_AGENT_HTTP_PROXY.

Add import 'global-agent/bootstrap'; to index.

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 26
  • Comments: 18 (1 by maintainers)

Commits related to this issue

Most upvoted comments

Hi @Niek - any ideas why the axios-https-proxy-fix fix aren’t merged into axios? I’m sure a lot of people are running into this.

set proxy to false can fix:

const axios = require('axios').create({ proxy: false })

Can confirm that issue using a http proxy for https connections -> export http_proxy=http://xyz:3330 -> export https_proxy=http://xyz:3330 -> curl https://www.google.com -> ok -> axios failed

Environment docker ubuntu 18.04

What I observed was that axios does recognise the environment variables, but the way it connects to the proxy causes issues.

I did try setting the proxy manually, but even that didn’t work and had the same issue.

The problem seems to be connecting to an HTTPS endpoint via an HTTP proxy.

Global agent uses the right way to setup the proxy and works, but axios’ default mechanism fails.

If you are using a non browser (NodeJS) process you should find that the below approach is needed. Most non browser processes (including C# and Java HTTP Clients) do not work off the HTTP_PROXY / HTTPS_PROXY environment variables and require an equivalent setting.

const options = {
  httpsAgent: TunnelAgent.httpsOverHttp({
    proxy: url.parse("http://127.0.0.1:8888");
  }),
};
await axios.get('https://github.com/', options);

It is common to control whether a process uses a proxy via a configuration file, as in this JSON file of mine.

This seems to be an issue since a long time. There’s even a “fixed” NPM package for this: https://www.npmjs.com/package/axios-https-proxy-fix Another one: https://www.npmjs.com/package/axios-proxy-fix

And a related PR: https://github.com/axios/axios/pull/3159

why cant axios just implement this? proxy shit is the most common shit out there… and proxy autodetect is NOT THAT HARD…

I combined two approaches, and it worked to me

npm i npm i tunnel-agent

axios
  .create({proxy:false})
  .get('https://xxxx', {
    headers:{authorization:'Bearer xxxx'}, 
    httpsAgent: TunnelAgent.httpsOverHttp({ proxy: url.parse("http://proxy:3128")})
  })

thanks @gary-archer and @linchen1987

because there is no real answer for that… what really works (for complex proxy scenarios)

const axios = require('axios');
const axiosHttpsProxy = require('helpers/axios.js');
axios.interceptors.request.use(axiosHttpsProxy);

and the content of the “helper”

var url = require('url')

var httpsProxyAgent = require('https-proxy-agent')

function proxy (config) {
  /* istanbul ignore if */
  if (config.socketPath != null) return config

  var parsed = url.parse(config.url)
  var protocol = parsed.protocol
  /* istanbul ignore if */
  if (protocol !== 'https:') return config

  var proxy = config.proxy
  /* istanbul ignore if */
  if (proxy === false) return config

  var proxyOptions
  if (proxy != null) {
    proxyOptions = {
      hostname: proxy.host,
      port: proxy.port
    }
    if (proxy.auth != null) {
      proxyOptions.auth = proxy.auth.username + ':' + proxy.auth.password
    }
  } else {
    var proxyUrl = process.env['HTTPS_PROXY'] || process.env['https_proxy']
    /* istanbul ignore if */
    if (!proxyUrl) return config

    parsed = url.parse(proxyUrl)
    proxyOptions = {
      hostname: parsed.hostname,
      port: parsed.port
    }
    if (parsed.auth) {
      proxyOptions.auth = parsed.auth
    }
  }

  // HTTPS request must use tunnel proxy protocol
  config.httpsAgent = httpsProxyAgent(proxyOptions)

  // Disable direct proxy protocol in axios http adapter
  config.proxy = false

  return config
}

module.exports = proxy

I noticed that my code above for a NodeJS (non browser process) was a little out of date. I was using an old Axios version, so I’ve updated it - here is some code of mine that calls a Web API via a proxy when required. It uses Axios version 0.21 currently and if behaviour changes for the next release I will update it.