Webpack-dev-server: devServer proxy not working

Created on 17 Feb 2017  路  47Comments  路  Source: webpack/webpack-dev-server

Do you want to request a feature or report a bug?
Bug

What is the current behavior?
devServer proxy doesn't proxy.

If the current behavior is a bug, please provide the steps to reproduce.
Client server running at localhost:8080 and API server running at localhost:8000. I've tried several variations of what to proxy (api/*, /api/**, api/**, etc). Use the following proxy settings:

  devServer: {
    hot: true,
    contentBase: resolve(__dirname, 'dist'),
    publicPath: '/',
    proxy: {
      '/api/**': {
        target: 'http://localhost:8000',
        secure: false,
        changeOrigin: true,
      }
    },
  },

What is the expected behavior?
Hitting /api/path should be proxied to http://localhost:8000/api/path but is instead hitting http://localhost:8080/api/path.

Please mention your webpack and Operating System version.

  • webpack @ 2.2.1
  • webpack-dev-server @ 2.3.0
  • macOS 10.12.3

Most helpful comment

replace localhost with [::1].
like so: 'http://[::1]:3000'

this works for me:

devServer: {
  proxy: {
    "*": "http://[::1]:8081"
    // "secure": false,
    // "changeOrigin": true
  }
},

one wildcard, or double, with 'secure' or 'changeOrigin' - doesnt matter. [::1] is the game changer.
Good luck!

All 47 comments

Have you tried '/api'?

@steve-taylor I have indeed. /api, api, /api/path, etc. Every variation I could think of.

I tried to reproduce this, but it does work for me. What's interesting though is that the error message I get in the browser is wrong. It says "Error occured while trying to proxy to: localhost:8080/api/users" even though the proxy is set to http://localhost:8000. When I start a server on port 8000 however, it does work correctly.

I wonder if there's a way to plug in a reverse proxy that actually works, rather than being handcuffed to http-proxy-middleware.

Yeah not sure.. Here's my whole config in case there's something else afoot. Maybe I've got something funky in the entry?

const { resolve } = require('path');
const webpack = require('webpack');

module.exports = {
  entry: [
    'react-hot-loader/patch',
    'webpack-dev-server/client?http://localhost:8080',
    'webpack/hot/only-dev-server',
    './app.js',
  ],

  output: {
    filename: 'bundle.js',
    path: resolve(__dirname, 'dist'),
    publicPath: '/',
  },

  context: resolve(__dirname, 'client'),

  devtool: 'inline-source-map',

  devServer: {
    hot: true,
    contentBase: resolve(__dirname, 'dist'),
    publicPath: '/',
    proxy: {
      '/api/**': {
        target: 'http://localhost:8000',
        secure: false,
        changeOrigin: true,
      }
    },
  },

  module: {
    rules: [
      {
        test: /\.js$/,
        use: [
          'babel-loader',
        ],
        exclude: /node_modules/
      },
      {
        test: /\.css$/,
        use: [
          'style-loader',
          'css-loader?modules',
          'postcss-loader',
        ],
      },
    ],
  },

  plugins: [
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NamedModulesPlugin()
  ],
};

@steve-taylor what specific issues are you having? I would suggest creating github issues on http-proxy-middleware for those. The author of http-proxy-middleware is very responsive and helpful.

@gjunkie can you make a repository with a minimal version of the backend you have on port 8000? That's the only way to be able to debug this further down.

@SpaceK33z I decided to shift over to use Hapijs' cors feature for my needs, however this is the repo I was attempting to proxy in. Beyond removing the proxy option, there haven't been other changes, in case this helps.

https://github.com/gjunkie/hapi-react-kit

I have the same problem as @gjunkie. Starting from v1.15.0 the http-proxy-middleware is installed as dependency and a ECONNRESET error is thrown each time I try to make a request to the backend.

Apparently, it is only happening in OS X environment because my colleagues at work are using Ubuntu and they can't reproduce this problem.

I could reproduce this bug on OS X. In Windows, it is working just fine.

I couldn't get devServer proxy to work either (OS X 10).
I'm running with a server.js which reads webpack.config.js and my workaround was a reverse proxy using http-proxy in server.js:

var httpProxy = require('http-proxy');
var apiProxy = httpProxy.createProxyServer();

// ... was here before
var app = express();
var compiler = webpack(config);

app.use(require('webpack-dev-middleware')(compiler, {
  publicPath: config.output.publicPath
}));

app.use(require('webpack-hot-middleware')(compiler));
// ... end of was here before

app.all("/api/*", function(req, res) {
    apiProxy.web(req, res, {target: 'http://localhost:3102'});
});

Reference: https://codeforgeek.com/2015/12/reverse-proxy-using-expressjs/

How to set proxy buffer, i want to upload some files to backend

Having the same issue here, also OSX

same issue on OSX

Same issue on OSX as well.

This configuration is working for me on OSX (I sweated blood to make it work). I have the GNU Coreutils installed, by the way (don't know if has something to do).

, proxy: [
          {
            path: /^\/styleguide(?!\/core-css\/build)/
          , target: 'http://localhost:8000'
          , bypass: function(req, res, _options) {
              return req.url.replace('styleguide', styleguideRoothPath + '/build');
            }
          }
        , {
            path: "**",
            target: "http://localhost:3000",
            secure: false,
            bypass: function(req, res, options) {
              if(req.url === '' || req.url === '/') {
                res.statusCode = 302;
                res.setHeader('Location', '/a/');
                return '/a/';
              }

              var frontend = new RegExp("^\/$^|\/a\/index\.html|^\/a\/|^\/a$|^\/styleguide");
              if (frontend.test(req.url)) return req.url;
            }
          }
        ]

Same issue on OSX.

replace localhost with [::1].
like so: 'http://[::1]:3000'

this works for me:

devServer: {
  proxy: {
    "*": "http://[::1]:8081"
    // "secure": false,
    // "changeOrigin": true
  }
},

one wildcard, or double, with 'secure' or 'changeOrigin' - doesnt matter. [::1] is the game changer.
Good luck!

Meet the same issue on mac, any progress here?

We've got a few workarounds posted for folks running into the issue. It seems that this might be slightly edge casey, however. If anyone would like to put together a Pull Request, we'd happily review it and revisit this issue.

In my case, I had to rewrite the path:

proxy: {
   '/api/**': {
      target: 'http://localhost:9596/',
      pathRewrite: { '^/api': '' },
      secure: false,
      logLevel: 'debug'
   }
}

Hope this can help.

I'm still having issues with this on Mac OSX and have tried everything above.
What else can I do to troubleshoot?

Related: https://github.com/webpack/webpack-dev-server/issues/458#issuecomment-325036559

EDIT: I think I was expecting the opposite behavior for some weird reason. By proxying the web server through the dev server, I should still be using the dev server, not the actual server while developing.

It's working for me now. Turned out I was just missing adding new webpack.HotModuleReplacementPlugin(), to the plugins array

Make sure that your request url and port matches that which your webpack-dev-server is running on. So, if your api is located at http://localhost:5000, and your dev server is running on http://localhost:8080, make sure all of your requests are to http://localhost:8080. Its best to make your requests to localhost:8080/api (to avoid conflict with app routes) and use the path rewrite to remove the /api.

Example:
Webpack devserver proxy config:

proxy: {
    '/api': {
        target: 'http://localhost:5000',
        pathRewrite: { '^/api': '' },
    },
}

Webpack dev server running on: http://localhost:8080
Desired API endpoint: http://localhost:5000/items

In your app, make the request to: http://localhost:8080/api/items.

This _should_ work. It seems to me that all of the above issues stem from making the request to the API url and port rather than the webpack dev server url and port and using the proxy and path rewrite to direct the request to the API.

@jrweinb, thanks for your clear explanation. I was trying to GET the API port like you had described, but it is still not working. In the console I have two errors,

GET http://localhost:8080/api/prospects 404 (NOT FOUND)
Uncaught (in promise) SyntaxError: Unexpected token < in JSON at position 0

The second error points to line 4 here, there is no '<' as described

export function fetchProspects() {
  return (dispatch) => {
    dispatch(requestProspects());
    return fetch('http://localhost:8080/api/prospects')
      .then(response => response.json())
      .then(json => dispatch(receiveProspects(json)));
  };
}

@withintheruins14 - (I deleted a previous reply as it was wrong). It looks like your API is not working. The 404 html is getting parsed in response => response.json()

* SOLVED *

@jrweinb, thanks for sticking with me - I am still trying to get this sorted. Yes, my request was 404ing but that is because the dev-server proxy isn't working. To me, my files appear in proper order, here they are:

actions/prospect.js

export function fetchProspects() {
  return (dispatch) => {
    dispatch(requestProspects());
    return fetch('http://localhost:8080/api/prospects')
      .then(response => response.json())
      .then(json => dispatch(receiveProspects(json.data)))
      .catch(ex => dispatch(requestProspectsFailure(ex)));
  };
}

webpack.config.js

const path = require('path');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const DIST_DIR = path.resolve(__dirname, "dist");
const SRC_DIR = path.resolve(__dirname, "src");

const config = {
  entry: [
    "babel-polyfill",
    SRC_DIR + "/app/index.js",
    SRC_DIR + "/app/assets/stylesheets/application.scss",
    "font-awesome/scss/font-awesome.scss",
  ],
  output: {
    path: DIST_DIR + "/app/",
    filename: "bundle.js",
    publicPath: "/app/"
  },
  devtool: 'inline-source-map',
  devServer: {
    contentBase: './dist',
    historyApiFallback: true,
    proxy: {
    '/api': {
        target: 'http://localhost:5001',
    },
}
  },
  module: {
    rules: [
      {
        enforce: "pre",
        test: /\.js$/,
        exclude: /node_modules/,
        loader: "eslint-loader",
        options: {
          failOnWarning: false,
          failOnError: true
        }
      },
      {
        test: /\.js$/,
        include: SRC_DIR,
        loader: 'babel-loader',
        query: {
          presets: ['react', 'stage-2']
        }
      },
      {
        test: /\.css$/,
        use: ExtractTextPlugin.extract({
          use: 'css-loader?importLoaders=1'
        })
      },
      {
        test: /\.scss$/,
        use: ExtractTextPlugin.extract(['css-loader', 'sass-loader'])
      },
      {
        test: /\.(jpe?g|png|gif|svg)$/i,
        loaders: ['file-loader?context=src/images&name=images/[path][name].[ext]', {
          loader: 'image-webpack-loader',
          query: {
            mozjpeg: {
              progressive: true,
            },
            gifsicle: {
              interlaced: false,
            },
            optipng: {
              optimizationLevel: 7,
            },
            pngquant: {
              quality: '75-90',
              speed: 3,
            },
          },
        }],
        exclude: /node_modules/,
        include: __dirname,
      },
      {
        test: /\.woff2?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
        // loader: "url?limit=10000"
        use: "url-loader"
      },
      {
        test: /\.(ttf|eot|svg)(\?[\s\S]+)?$/,
        use: 'file-loader'
      },
    ]
  },
  plugins: [
    new ExtractTextPlugin({
      filename: "application.css",
      allChunks: true
    })
  ]
};

module.exports = config;

package.json

"scripts": {
    "test": "jest",
    "watch": "webpack --progress --watch",
    "start": "npm run build",
    "build": "webpack -d && cp src/index.html dist/index.html && webpack-dev-server --inline --hot --history-api-fallback",
    "build:prod": "webpack -p && cp src/index.html dist/index.html"
  }

Thanks!

The above was solved. My problem was on the server and not within my webpack.config.js. Our Flask server prevents all requests from different servers when a server name is specified. To fix this, we simply left the server name unspecified in config.py. Thanks for your help @jrweinb

I faced similar issue, however my issue was:
My UI ran on: localhost:9000
My API ran on localhost:3000 and endpoint /api/**

My proxy config:

devServer: {
    proxy: {
        '/api/**': {
            target:'http://localhost:3000'
        }
    }
}

My problem was - when I was making API calls from my React UI, i was pointing it to locahost:3000/api/** whereas I need to point it at the localhost:9000/api/** so that i can proxy it from the local domain to the remote domain.

Been scratching my head since Monday trying to figure this out! This ended up working for me:

proxy: {
    '**': {
        target: 'http://localhost:8080',
        secure: false
    }
}

I have webpack-dev-server running on port 3000, and Express running on port 8080. Requests to the API are made via http://localhost:3000/api/<whatever>

@jellyfish-tom your solution did it for me with Mac OSX 10

replace localhost with [::1].
like so: 'http://[::1]:3000'

this works for me:

devServer: {
  proxy: {
    "*": "http://[::1]:8081"
    // "secure": false,
    // "changeOrigin": true
  }
},

@italoborges You saved my life:)

pathRewrite: {'^/api': ''}

I ran into the same issue and found

new CopyWebpackPlugin([
      {
        from: path.resolve(__dirname, '../static'),
        to: config.dev.assetsSubDirectory,
        ignore: ['.*']
      }
    ])

in webpack.dev.conf.js .
Seems, it changes the way, proxyTable is handling the path to static??
I commented it out, restarted npm run dev and all is running fine again.
Is there a better way to handle this than deleting the plugin?

BTW. This is my config for proxytable (now working again):

proxyTable: {
            '/static': {
                target: 'http://firedev.local:8888',
                changeOrigin: true,
                logLevel: 'debug'
            }
        },

I am also facing this issue
I am trying to be solving it till this morning but with no success
I've tried every solution but nothing is working for me
can anyone help please?

I'm having a similar problem with the proxy - but same here, nothin is workin. I have a repo I could share: https://github.com/writesandy/SSMRB

@jellyfish-tom thanks

Now mine is working, here's my config:

devServer: {
historyApiFallback: true,
contentBase: './dist',
watchOptions: {
aggregateTimeout: 500,
poll: 1000,
ignored: /node_modules/
},
proxy: { '/api': { target: 'http://[::1]:3030', secure: false } }
}

For Webpack 4 and OSX10, I had to add changeOrigin property to make it work.

{
    historyApiFallback: true,
    contentBase: './static',
    port: 9095,
    proxy: {
        '/api/**': {
            target: 'http://another-url',
            pathRewrite: {
                '^/api': '',
            },
            secure: false,
            changeOrigin: true,
            logLevel: 'debug',
        },
    },
};

For us setting the header Connection: keep-alive did the trick. Requests were handled by the backend server and a response was sent as well, nevertheless we saw the error ECONNRESET for some of the API requests before.

'api' : {
  target: SERVER_URL,
  secure: false,
  changeOrigin: true,
  headers: {
    Connection: 'keep-alive',
  },
}

Our UX guy has also been suffering the ECONNRESET on macOS for some time and he's tried most things to solve it, however the solution from @SilentGert has solved the problem.

If this is helpful for any one browsing the topic, this setup works for me on webpack 4.33.0 and webpack-dev-server 3.7.1.

module.exports = {
  entry: [
    'webpack-dev-server/client?http://localhost:8080',
    'webpack/hot/dev-server',
    './src/index.tsx'
  ],
  devtool: 'source-map',
  devServer: {
    contentBase: path.resolve(__dirname, 'dist'),
    hotOnly: true,
    progress: true,
    port: 8080,
    proxy: {
      '/api': 'http://localhost:4000',
    },
    open: true,
    watchContentBase: true,
    inline: true,
    historyApiFallback: true,
    stats: 'minimal'
  },
...
}

I'm using osX and these dependencies _(if you find it relevant to your setup)_:

"devDependencies": {
    "@types/react": "^16.8.19",
    "@types/react-dom": "^16.8.4",
    "@types/react-router-dom": "^4.3.3",
    "@types/webpack-env": "^1.13.9",
    "html-webpack-plugin": "^3.2.0",
    "ts-loader": "^6.0.2",
    "typescript": "^3.5.1",
    "webpack": "^4.33.0",
    "webpack-bundle-analyzer": "^3.3.2",
    "webpack-cli": "^3.3.4",
    "webpack-dev-server": "^3.7.1"
  },
  "dependencies": {
    "react": "^16.8.6",
    "react-dom": "^16.8.6",
    "react-router-dom": "^5.0.1"
  }

I also noticed that using the proxy option with historyApiFallback prevents the latter from working (500 Internal Server Error)... An issue for another day I suppose.

@jrweinb thank you! it works!

proxy to http://some.localhost:8888 not working! Just ENOTFOUND

This should work:

devServer: {
    contentBase: path.join(__dirname, "/dist"),
    historyApiFallback: true,
    publicPath: '/',
    inline: true,
    port: 8080,
    stats: { colors: true },
    hot: true,
    proxy: {
      '/api/**': {
        target: 'http://localhost:44310',
        pathRewrite: { '^/api': '' },
        secure: false,
        changeOrigin: true
      }
    }
}

replace _localhost_ with _[::1]_.
like so: 'http://[::1]:3000'

this works for me:

devServer: {
  proxy: {
    "*": "http://[::1]:8081"
    // "secure": false,
    // "changeOrigin": true
  }
},

one wildcard, or double, with 'secure' or 'changeOrigin' - doesnt matter. [::1] is the game changer.
Good luck!

I have tried, but getting this issue
Error occurred while trying to proxy request /api/v3/sign-in from localhost:9898 to http://[::1]:8081 (ECONNREFUSED) (https://nodejs.org/api/errors.html#errors_common_system_errors)

This should work:

devServer: {
    contentBase: path.join(__dirname, "/dist"),
    historyApiFallback: true,
    publicPath: '/',
    inline: true,
    port: 8080,
    stats: { colors: true },
    hot: true,
    proxy: {
      '/api/**': {
        target: 'http://localhost:44310',
        pathRewrite: { '^/api': '' },
        secure: false,
        changeOrigin: true
      }
    }
}

getting error "http://localhost:9898/api/v3/sign-in 404 (Not Found)"
till it using vue url (localhost:9898), its not proxy the given url(localhost:3337) for api request
vue js port : 9898
backend api port: 3337

In my case it doesn't work when a file that should be proxied exists.
For example, request to /api/auth.php
/public/api/auth.php - Will not work if the file above exists

Solution for this stack: macOS 10.14.6, docker 20.10.0, docker-compose 1.27.4, webpack 4.38.0, preact-cli 3.0.3.

// Example of a webpack config for a preact application.
// preact.config.js

export default {

  webpack(config, env, helpers, options) {

    // "Gateway" is my Nginx service that routes API requests 
    // and static files inside my Docker network.

    let target = 'http://gateway'

    config.devServer.proxy = [
      {
        path: '/api/v1',
        target
      },
      {
        path: '/static',
        target
      }
    ]

  }

}
// Example of a client-side request.

fetch(`/api/v1/service-name/test`)
  .then((res) => res.json())
  .then((data) => {
    console.log(data)
  })
  .catch(err => {
    console.error(err)
  })
// Example of connecting a static file.
<img src='/static/images/test.jpg' />
Was this page helpful?
0 / 5 - 0 ratings

Related issues

StephanBijzitter picture StephanBijzitter  路  3Comments

Ky6uk picture Ky6uk  路  3Comments

uMaxmaxmaximus picture uMaxmaxmaximus  路  3Comments

adiachenko picture adiachenko  路  3Comments

antoinerousseau picture antoinerousseau  路  3Comments