Browsersync on remote development VM will not work in local browser - javascript

Thank you so much for taking the time to check out my little conundrum. This is my first StackOverflow question, so please forgive me if I accidentally leave out something important (I'll add any information that you require if you just let me know).
My issue is this: I have a personal development VM that I use to house all of my application code, including the application's server. I use PuTTY to connect to my development VM, and I forward the port that the vert.x application server runs on (which is 7443) to my localhost. Thus, when I connect to the server, I just enter:
https://localhost:7443
in the URL bar of the browser on my local machine. I also use Gulp.js to build the UI component and watch my files for changes. Currently, the gulp watch task just rebuilds Javascript and CSS bundle files that I link to in my index.html file, and I manually reload the browser whenever the bundles are done rebuilding. I recently stumbled upon Browsersync when I was researching live-reload technologies for the browser, and it looks awesome, but I cannot get it to work with my development setup.
Here is my gulp watch task:
const browserSync = require("browser-sync").create("browser-sync-server");
gulp.task("watch", () => {
console.info("=== Initializing BrowserSync Server ===");
browserSync.init({
proxy: "localhost:7443", // This is my dev server running on port 7443
open: false // I do not want a new browser to open up when I start BrowserSync
});
console.info("=== Listening for changes to reload === ");
const jsWatcher = gulp.watch(
myJsFiles,
{ awaitWriteFinish: true },
["reload-js"]
);
const templateWatcher = gulp.watch(
myHtmlFiles,
{ awaitWriteFinish: true },
["reload-templates"]
);
const lessWatcher = gulp.watch(
myLessFiles,
{ awaitWriteFinish: true },
["reload-less"]
);
});
When I start the watch task, in the console I see:
Starting 'watch'...
=== Initializing BrowserSync Server ===
=== Listening for changes to reload ===
Finished 'watch' after 126 ms
[Browsersync] Proxying: http://localhost:7443
[Browsersync] Access URLs:
Local: http://localhost:3000
External: http://192.168.1.11:3000
UI: http://localhost:3001
UI External: http://192.168.1.11:3001
I also forward ports 3000 and 3001 to my local machine so that I can access those endpoints in my browser.
I can get to the Browsersync UI just fine at port 3001 over HTTP, but when I click on the "NEW TAB" button under Local, a new tab pops, but the server never loads. It just spins and spins, until finally I get an error in Chrome that says "No data received". Does anyone have any ideas as to what the problem could be? My guess is that is has something to do with the fact that my team's application uses HTTPS for browser access, and that Browsersync needs some further configuration to work with HTTPS, but I do not know how to go about doing this.
Thank you all so much for helping me out! Please let me know if I can provide you all with any more information.
-- Tom
Edit 5/23/18:
I used openssl to generate localhost key and cert files for my development VM, and added them to my configuration for BrowserSync in my gulpfile.js.
Here is my modified config:
const proxy = require("http-proxy-middleware");
...
browserSync.init({
ui: {
port: 8080
},
https: {
key: "./conf/browsersync/localhost.key",
cert: "./conf/browsersync/localhost.crt",
},
server: {
baseDir: "./",
index: "index.html"
},
middleware: [
proxy("/api", {
target: "https://localhost:7443",
secure: false, // Do not validate SSL certs
changeOrigin: true, // Seems to be a highly recommended setting
xfwd: true,
prependPath: true, // Ensure that the API calls are prepended with the target URL
logLevel: "debug" // So that I can see verbose console output
})
],
port: 3000,
open: false
});
This has definitely gotten me farther, but it is still not quite right. Now, when I hit https://localhost:3000 in my browser, I am taken to the index page of my web application, but none of the asynchronous API calls are getting resolved correctly. In the console where I am running the gulp watch task, I see a lot of errors from HPM (the html-proxy-middleware software). Here is one example:
[HPM] Error occurred while trying to proxy request /api/settings/ from localhost:3000 to https://localhost:7443 (ECONNRESET) (https://nodejs.org/api/errors.html#errors_common_system_errors)
Also, if I open the Javascript console in my browser window for https://localhost:3000 (the BrowserSync session), I can see lots of 504 errors (Gateway Timeout). Any ideas? Thanks again so much for your time.
-- Tom

First of all, I agree, browsersync is awesome.
[Browsersync] Proxying: http://localhost:7443 Note the http instead of https. This cannot work. In your line proxy: "localhost:7443" you are not telling anything about the protocol. Try with a https:// prefix. Most likely you'll also need the "secure: false" or similar if your development server uses a self-signed certificate. The proxy will have to accept this certificate, not your browser.
If your backend also uses secure-only cookies, you'll have to use https on the browsersync and client side too, or your browser will refuse the secure cookie. Here is my working development config (Gruntfile syntax) doing that:
var proxy = require('http-proxy-middleware');
[...]
options: {
port: 3000,
server: {
baseDir: './',
index: '_index.html'
},
// redirect all calls to /api to the backend
https: true, // required for secure cookie to work
middleware: [proxy('/api', {
target: "https://external.backend.server",
secure: false, // required if server uses self-signed certificate
changeOrigin: true,
xfwd: true // add X-Forwarded-* headers
})],
watchTask: true,
logConnections: true,
scrollRestoreTechnique: 'cookie',
reloadDebounce: 500,
ghostMode: false
}

Related

nuxt.js - unable to define host and port

nuxt.js always defaults to localhost despite host being defined as frontend.gradez.loc in nuxt.config.js
Contents of nuxt.config.js:
server: {
host: 'frontend.gradez.loc',
port: 3000
},
Contents of package.json:
"config": {
"nuxt": {
"host": "frontend.gradez.loc",
"port": "3000"
}
}
nuxt launch script as dev:
"dev": "nuxt --hostname frontend.gradez.loc --port 3000",
For some odd reason when starting the development script it always defaults to: Listening on: http://localhost:3000/
I tried to do exactly the same on react and the only thing I had to do was create a .env file and inside it I added host=frontend.gradez.loc and it worked just like that.
To create your server, under the hood Nuxt uses Node's http.createServer, then calls listen({ host, port }) on that server.
So if on your local machine the hostname frontend.gradez.loc is mapped to 127.0.0.1, which I assume is the case, then that server is running on the IP 127.0.0.1.
To create the url you see printed in Listening on..., Nuxt gets the IP of that underlying server, and maps it back to a hostname string. It statically maps the IP 127.0.0.1 to the string 'localhost', so no matter what host you configure, if it maps to 127.0.0.1 then Nuxt will always map that to localhost in that url. The code that does this is here.
There's nothing incorrect per-se about reporting the server is running on localhost:3000 rather than frontend.gradez.loc:3000. It's literally true, in a networking sense, because both ultimately point to 127.0.0.1:3000. So nothing is broken here from the perspective of the dev server, it's working as designed.
I'm not sure if you have anything automatically loading that url in the browser when you start the server - if so I can see how this is inconvenient from the perspective of other things in your workflow coupled to that hostname such as cookies, proxy servers etc - but if you manually type frontend.gradez.loc:3000 into your browser everything will just work.

Webpack proxy w/ Wordpress not redirecting GET requests inside page

I'm trying to create a Wordpress theme development environment (more precisely migrating from gulp w/ browser-sync to webpack). Therefore I've set up a proxy pointing to my wordpress installation (also locally). When I access the webpack dev server in the browser the proxy seems to be working fine. For instance localhost:8080/sample-page shows the WordPress page localhost:8000/sample-page.
The problem is, that any links inside that page will point back to the original port. When I use the navigation on the page I get redirected back to localhost:8000/*.
With gulp, I only had to create the browser-sync task with one option being the target URL and it worked perfectly:
browserSync.init({
proxy: pjson.themeURL || "localhost:8000",
open: false
});
I have tried to change different options of the HTTP-proxy-middleware, currently, I got the following config (I also tried options like an auto rewrite, etc.):
devServer: {
publicPath: `${options.publicPath}/build/`,
proxy: {
"*": {
target: pjson.proxyURL || "http://localhost:8000",
logLevel: "debug",
secure: false,
changeOrigin: true
}
}
},
The publicPath will resolve to /wp-content/themes/theme-name/build/ (I also tried including the whole url not just the path)
I hope I can get it to run just like the browser-sync proxy did. Maybe I need to set some headers?
I appreciate all the help!
EDIT:
I have found the following code from the browser-sync source which seems to have a method responsible for replacing all the links inside the page:
https://github.com/BrowserSync/browser-sync/blob/master/packages/browser-sync/lib/server/proxy-utils.js
The Dev server seems to have the possibility to modify a request with the onProxyRes hook where I could do something like modifiying all the links, I just didn't have the time yet to try it out, so I now using browser-sync atm.
I've not found a solution yet, my current workaround is to use webpack -watch and browser-sync without webpack-dev-server, this way hot reloading won't work though :/
My webpack config plugins look currently like this:
plugins: [
new BrowserSyncPlugin({
port: pjson.themeConf.port || 3000,
proxy: pjson.themeConf.proxyURL || "localhost:8000",
files: ["**.php"]
}),
new MiniCssExtractPlugin({})
],

How to disable browsersync in Aurelia application?

I created an Aurelia application using aurelia-cli.
I need to disable the browsersync option and also the notifications generated by it. Like "Connected to browser sync" Like that.
Is there any way to do that ?
When you build your application for production, it will not include Browser Sync.
Instead of the normal
> au run --watch
You'll use
> au build --env prod
After that, you'll need to serve up your application through a traditional web server. If you've correctly bundled it, you'll only need index.html and your scripts folder.
just as a dev side note:
browsersync options are available at localhost:3001 (unless you changed default port)
there you can do things like turn off mirroring clicks and scrolls in all instances of your locally running app (useful when you would like to open two or more different routes in your local app in multiple tabs/browsers) or monitor history of all routes you have visited in your local app.
You can also disable the ghost mode in aurelia_project/tasks/run.js.
Just add ghostMode: false to the browserSync configuration.
let serve = gulp.series(
build,
done => {
browserSync({
online: false,
open: false,
port: 9000,
logLevel: 'silent',
ghostMode: false,
server: {
baseDir: ['.'],
middleware: [historyApiFallback(), function(req, res, next) {
res.setHeader('Access-Control-Allow-Origin', '*');
next();
}]
}
}, function(err, bs) {
let urls = bs.options.get('urls').toJS();
log(`Application Available At: ${urls.local}`);
log(`BrowserSync Available At: ${urls.ui}`);
done();
});
}
);
This disables the synchronization of interaction events between browsers. At least for me, this is often the most annoying part.

Manually rewrite requests to specific URLs in Browsersync

I'm using BrowserSync in a somewhat strange setup, where I'm proxying my web server (Apache in a Docker container), but also serving hot module replacement (HMR) from a Webpack dev server.
In my local dev environment, the setup looks something like this:
https://mytestsite.localhost – an Apache service in a Docker container
https://localhost:8888 – Webpack Dev server, serving HMR
https://localhost:3000 – BrowserSync`
For hard reloads, this all works fine – the webpack dev server appears to pass on the message that a reload is needed and all is well.
The issue that I'm having is with hot reloads. The document being served by the BS proxy should be reading the hotupdate.json served by webpack-dev-server. On receiving a hot update, the page tries to load /hotupdate.json (which I believe tells it which snippet of code to pick up), however, because it's a relative URL, the browser tries to GET https://localhost:3000/hotupdate.json, which 404s, because this hotupdate.json is actually served by the Webpack server, e.g. https://localhost:8888/hotupdate.json.
Because I know the absolute URL to this resource, I'd like to force BrowserSync to redirect any requests to /hotupdate.json to https://localhost:8888/hotupdate.json. I thought I could do this with some middleware, but I'm struggling, possibly because I've never fully groked Express-style middleware.
I've tried something like this, but no worky!
browserSync({
proxy: {
target: `https://${process.env.APP_HOST_PATH}`,
middleware: [
dontProxyHotUpdate,
require('webpack-dev-middleware')(bundler, {
noInfo: true,
publicPath: webpackConfig.output.path
}),
]
},
files: [
'app/css/*.css',
'app/*.html'
]
});
function dontProxyHotUpdate (req, res, next){
if(req.url === '/hotupdate.json'){
req.url = 'https://127.0.0.1:8888/hotupdate.json';
}
next();
}
It definitely loads the middleware as I can, say, console.log(req.url), but I'm not able to rewrite the request URL. I suppose possible solutions would either be to rewrite the request URL, or to overwrite the response directly.
N.B. One might ask why I'm not using webpack-dev-server directly, as it serves HMR nicely on its own. It does, but it also doesn't allow for nice rewriting of the anchor elements within a page, e.g. changing https://mytestsite.localhost/link to https://localhost:3000/link. This is important obviously for navigating through a site while developing (which is nice, but not essential), but even more important for rewriting links to assets – SVGs, in particular, which won't load unless the path, host and port all match.
Well, in the end I sorted my own problem!
I ended up writing my own middleware using http-proxy-middlware - like this.
var proxy = require('http-proxy-middleware');
browserSync({
proxy: {
target: `https://${process.env.APP_HOST_PATH}`,
middleware: [
dontProxyHotUpdate,
require('webpack-dev-middleware')(bundler, {
noInfo: true,
publicPath: webpackConfig.output.path
}),
// require("webpack-hot-middleware")(bundler) // I don't think that we want this here as it can be handled by the webpack dev server
],
},
// no need to watch '*.js' here, webpack will take care of it for us,
// including full page reloads if HMR won't work
files: [
path.join(source, '**/*.php'),
path.join(source, 'style.css')
]
});
var dontProxyHotUpdate = proxy('/hotupdate*', {
target: 'https://127.0.0.1:8888/',
changeOrigin: true, // for vhosted sites, changes host header to match to target's host
logLevel: 'debug',
secure: false
});

How to get access to webpack-dev-server from devices in local network?

There is some webpack dev server config (it's part of the whole config):
config.devServer = {
contentBase: './' + (options.publicFolder ? options.publicFolder : 'public'),
stats: {
modules: false,
cached: false,
colors: true,
chunk: false
},
proxy: [{
path: /^\/api\/(.*)/,
target: options.proxyApiTarget,
rewrite: rewriteUrl('/$1'),
changeOrigin: true
}]
};
function rewriteUrl(replacePath) {
return function (req, opt) { // gets called with request and proxy object
var queryIndex = req.url.indexOf('?');
var query = queryIndex >= 0 ? req.url.substr(queryIndex) : "";
req.url = req.path.replace(opt.path, replacePath) + query;
console.log("rewriting ", req.originalUrl, req.url);
};
}
I execute webpack with the following command:
node node_modules/webpack-dev-server/bin/webpack-dev-server.js --host 0.0.0.0 --history-api-fallback --debug --inline --progress --config config/webpack.app.dev.js
I can get access to dev server using http://localhost:8080 on my local machine, but I also want to get access to my server from my mobile, tablet (they are in the same Wi-Fi network).
How can I enable it? Thanks!
(If you're on a Mac and network like mine.)
Run webpack-dev-server with --host 0.0.0.0 — this lets the server listen for requests from the network, not just localhost.
Find your computer's address on the network. In terminal, type ifconfig and look for the en1 section or the one with something like inet 192.168.1.111
In your mobile device on the same network, visit http://192.168.1.111:8080 and enjoy hot reloading dev bliss.
You can set your ip address directly in webpack config file:
devServer: {
host: '0.0.0.0',//your ip address
port: 8080,
disableHostCheck: true,
...
}
It may not be the perfect solution but I think you can use ngrok for this.
Ngrok can help you expose a local web server to the internet.
You can point ngrok at your local dev server and then configure your app to use the ngrok URL.
e.g Suppose your server is running on port 8080. You can use ngrok to expose that to outer world via running
./ngrok http 8080
Good thing about ngrok is that it provides a more secure https version of exposed url which you give to any other person in the world to test or show your work.
Also it has lots of customization available in the command such as set a user friendly hostname instead of random string in the exposed url and lots of other thing.
If you just want to open your website to check mobile responsiveness you should go for browersync.
For me, what helped eventually was adding this to the webpack-dev-server config:
new webpackDev(webpack(config), {
public: require('os').hostname().toLowerCase() + ':3000'
...
})
and then also changing babel's webpack.config.js file:
module.exports = {
entry: [
'webpack-dev-server/client?http://' + require('os').hostname().toLowerCase() + ':3000',
...
]
...
}
Now just get your computer hostname (hostname on OSX's terminal), add the port you defined, and you're good to go on mobile.
Compared to ngrok.io, this solution will also let you use react's hot reloading module on mobile.
I could not comment in order to add additional information to forresto's answer, but here in the future (2019) you'll need to add a --public flag due to a security vulnerability with --host 0.0.0.0 alone. Check out this comment for more details.
In order to avoid "responding to other answers" as an answer here's forresto's advice plus the additional details you'll need to make this work:
Add both:
--host 0.0.0.0
and
--public <your-host>:<port>
where your-host is the hostname (for me it is (name)s-macbook-pro.local)) and port is whatever port you're trying to access (again, for me it's 8081).
So here's what my package.json looks like:
"scripts": {
...
"start:webpack": "node_modules/.bin/webpack-dev-server --host 0.0.0.0 --public <name>s-macbook-pro.local:8081",
...
},
I found this thread while searching for a solution that would satisfy the following requirements:
automatically open URL using the public IP. For example, http://192.168.86.173:8080. So it could be copied from the browser and sent to another device in the network.
dev server is available in the local network.
Webpack 4
devServer: {
host: '0.0.0.0',
useLocalIp: true,
}
Webpack 5
devServer: {
host: 'local-ip',
}
With webpack-dev-server v4.0.0+ you need
devServer: {
host: '0.0.0.0',
port: 8030,
allowedHosts: ['all'] // or use 'auto' for slight more security
}
If you tried everything stated in the other answers here, without success... also make sure there's no firewall running on you machine(s) or open the needed ports on it.

Categories