Heroku nodejs- redirect HTTP to HTTPS - node.js

I am working on Progressive Web App and when testing it with Lighthouse I get:
Does not redirect HTTP traffic to HTTPS
My server is in nodejs and it is on Heroku. I was trying to add this code:
app.get('*',function(req,res,next){
if(req.headers['x-forwarded-proto']!='https'){
res.redirect(`https://${req.header('host')}${req.url}`)
}else{
next()
}
})
But it didn't help. Any ideas how to solve that?

You want to enforce this on the server, and the easiest way to do that is to use the express-sslify module. From the documents:
var express = require('express');
var http = require('http');
var enforce = require('express-sslify');
var app = express();
// Use enforce.HTTPS({ trustProtoHeader: true }) since you're behind Heroku's reverse proxy
app.use(enforce.HTTPS({ trustProtoHeader: true }));
// The rest of your express config.
Note also that you want to employ app.use() as I did, rather than app.get() as you did in order to make sure you're covering all requests to your app, not just GETs.

listen on port 80 and redirect to https
var http = require('http');
var server = http.createServer((req, res) => {
res.writeHead(301,{Location: `https://${req.headers.host}${req.url}`});
res.end();
});
server.listen(80);

Why not check req.protocol?
app.get('*',function(req,res,next){
if(req.protocol !== 'https'){
res.redirect(`https://${req.header('host')}${req.url}`)
}else{
next()
}
})

Another solution I have been using with no apparent drawbacks is to use the node package ssl-express-www, and that is assuming you are using Express which it looks like you are.
The code is extremely simple:
First install the package npm i ssl-express-www
Then in your server file:
const secure = require("ssl-express-www")
const app = express();
app.use(secure)
That's literally it! Put that code at the top before your route handlers and it will do all the 'heavy-lifting' for you.

I found the solution. As I was using React, I just created file, on client side, called static.json in the main folder, and added that:
{
"root": "build/",
"https_only": true,
"headers": {
"/**": {
"Strict-Transport-Security": "max-age=7776000"
}
}
}

Related

How to install SSL for the following setup (React Frontend + Nodejs Backend + Custom Domain Heroku)

General information about my setup
Currently I am building a web application using react and a nodejs API that is providing the data for this web application. Both apps are hosted on heroku.com and run independently from each other. I have bought a custom domain from a different hosting provider and used the heroku custom domain option to point the DNS to my website.
Technical details about my setup
NodeJS server: Express
NodeJS version: v10.15.0
React version: v16.2.0
Custom domain: www.tabbs.nl
Heroku domain: tabbs-web-app.herokuapp.com
The issue I am experiencing
I have been digging into a lot of documentation and tutorials in order to setup SSL for react / NodeJS but couldn't find a decent tutorial about how to set SSL / security for my setup.
Tutorials I already have read:
Node + Express + Lets Encrypt
How to use SSL/TLS with nodejs
Stack overflow posts and probably a whole lot more I am forgetting right now.
What do I want to achieve?
The goal I would like to achieve is setting up a secure connection between React web application (frontend) and NodeJS API (backend) so that all data between those is encrypted and safe. Also I want my custom domain (bought by a different hosting provider than Heroku) to be secure and forced using https.
For any questions or additional information please do not hesitate to ask!
Have you tried using the https module in node?
You can do something like this:
var express = require('express');
var https = require('https');
var http = require('http');
var app = express();
http.createServer(app).listen(80);
https.createServer(options, app).listen(443);
The app returned by express() is in fact a JavaScript Function, designed to be passed to Node’s HTTP servers as a callback to handle requests. This makes it easy to provide both HTTP and HTTPS versions of your app with the same code base, as the app does not inherit from these (it is simply a callback.
If you are using create react app, open your terminal and type “npm run build”. This creates a build folder with all of your static files.
Now go back to your node backend service and add the following:
var express = require('express');
var path = require('path');
var https = require('https');
var http = require('http');
var app = express();
const options = {
key: fs.readFileSync("/srv/www/keys/my-site-key.pem"),
cert: fs.readFileSync("/srv/www/keys/chain.pem")
};
http.createServer(app).listen(80);
https.createServer(options, app).listen(443);
app.use(express.static(path.join(__dirname, 'build')));
app.get('/', function(req, res) {
res.sendFile(path.join(__dirname, 'build', 'index.html'));
});
If you’re using react router to handle routing for you web app then you would amend the GET request as such:
var express = require('express');
const path = require('path');
var https = require('https');
var http = require('http');
var app = express();
const options = {
key: fs.readFileSync("/srv/www/keys/my-site-key.pem"),
cert: fs.readFileSync("/srv/www/keys/chain.pem")
};
http.createServer(app).listen(80);
https.createServer(options, app).listen(443);
app.use(express.static(path.join(__dirname, 'build')));
app.get('/*', function(req, res) {
res.sendFile(path.join(__dirname, 'build', 'index.html'));
});
This ain't a complex issue, do not worry about ssl, just create your own certificate for Node.JS/Express and that's enough.
and, React has a built-in way of doing api calls,
add this line to package.json of your React installation,
"proxy": "http://localhost:8000/"
and just call the api service like this,
//Generic API Call
callApi = async () => {
const response = await fetch('/api/hello');
const body = await response.json();
if (response.status !== 200) throw Error(body.message);
return body;
};
// A Submit handler to proxy
handleSubmit = async e => {
e.preventDefault();
const response = await fetch('/api/myrequest', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ post: this.state.post }),
});
const body = await response.text();
this.setState({ responseToPost: body });
};
it all works.

How to Proxy using Node using express-http-proxy to strip final rest

On my local JavaScript, I want to make a rest call to
/rest/speakers
and have that proxied to
http://localhost:2011/rest/speakers
I have code as follows that does not quite work as I want:
var proxy = require('express-http-proxy');
var app = require('express')();
app.use('/rest', proxy('localhost:2011/'));
app.listen(8081, function () {
console.log('Listening on port 8081');
});
To make the proxy work, I actually need to call
/rest/rest/speakers
I kind of get it. It seems that I'm proxying my /rest to the root of localhost:2011 and then I need to dive into the /rest of that server. Adding /rest to the end of the proxy(..) is ignored.
Try using proxyReqPathResolver to modify your URL as needed.
var url = require('url'),
proxy = require('express-http-proxy');
// ... other app setup here
app.use('/rest', proxy('localhost:2011', {
proxyReqPathResolver: function(req, res) {
return '/rest/rest' + url.parse(req.url).path;
}
}));

node.js express + aws elb - redirect from http to https not working

I know that this has already been asked and answered here but still no solution has worked for me. Here is the app.js:
var express = require('express');
var app = express();
app.all(function(req, res, next){
if((!req.secure) && (req.get('X-Forwarded-Proto') !== 'https'))
{
console.log (req.get('Host'));
console.log("not secure");
res.redirect('https://' + req.get('Host') + req.url);
}
else{
console.log("secure");
next();
}
}
);
app.use(express.static('./server/static/'));
app.listen(8080);
I have tried using app.use as well but still it doesn't work.
We are using AWS ELB and here are the listeners:
LB Protocol | LB Port | Instance Protocol |Instance Port
HTTPS | 443 | HTTP | 8080
HTTP | 80 | HTTP | 8080
As mentioned in other posts, I do not see 'x-forwarded-proto' in my request headers. I believe the listeners have been configured correctly so that they should have included 'x-forwarded' headers but that's not the case.
When I try hitting the dns without https, it doesn't get redirected to https.
However, both http and https urls work fine individually.
Please help me figuring out what I am missing or if any more information needs to be provided.
You might want to check for both 'X-Forwarded-Proto' and 'x-forwarded-proto' (lowercase). Also, the request will never be secure inside your application, so you should also remove the req.secure check. The final code should look something like this:
function forceHttps(req, res, next) {
const xfp =
req.headers["X-Forwarded-Proto"] || req.headers["x-forwarded-proto"];
if (xfp === "http") {
const secureUrl = `https://${req.headers.hostname}${req.url}`;
res.redirect(301, secureUrl);
} else {
next();
}
}
We use this code for our Express application behind Elastic Load Balancer, and it works fine. We even created a small NPM package that handles this, if you don't want to write the middleware yourself:
https://www.npmjs.com/package/#crystallize/elasticloadbalancer-express-force-https
Install
npm i --save #crystallize/elasticloadbalancer-express-force-https
Usage
const express = require('express');
const forceHttps = require('#crystallize/elasticloadbalancer-express-force-https');
const server = express();
server.use(forceHttps());
More information in our blog post about this:
https://snowball.digital/blog/force-https-behind-elastic-load-balancer-on-a-nodejs-application

Manually injecting express app via `http.createServer` proxy

I am currently authoring a component for a users sites which adds a number of resources to their server. For my part, I am wanting to use express, but the user could be using express themselves or some other web framework.
Because I want this to just work out of the box, I was attempting to setup my express pipeline via proxying http.createServer. Injecting the pipeline this way seems to work reasonably well in cases where I handle the request, but it fails in cases where I let it fall through and execute the users callback (specifically, it says that the response headers have been sent already).
Here is the sample I am working on. Any ideas?
var http = require('http');
var express = require('express');
var setupProxy = function setupProxy() {
var app = buildApp();
var oldCreateServer = http.createServer;
http.createServer = function(callback) {
return oldCreateServer(function(req, res) {n
app.apply(this, arguments);
if (!res.finished) {
callback.apply(this, arguments);
}
});
};
};
var buildApp = function buildApp() {
var app = express();
app.use('/test', function(req, res) {
res.send('Hello World');
});
return app;
};
I suspect your express handling creates a default 404 response when it doesn't match any of your routes. So, that would be the cause of the headers already sent issue when you are not handling it, but trying to pass it on.
So, I think you need your own 404 handler that writes nothing to the request (e.g. does nothing), but keeps Express from handling it.
Or, another possibility would be to call the user's server callback from your express 404 handler and not elsewhere.

Ajax request from another domain using AngularJs

I'm using this book to learn AngularJS where I build this webapp with Angular, Node, Deployd. Now, my app stays at localhost:5000/app.html, 5000 is the port where node web server listen. I try to retrieve my data stored with deployd in this way:
$http.get("localhost:5500/products")
.success(function (data) {
$scope.data.products = data;
})
.error(function (error) {
$scope.data.error = error;
});
But this cause an error: no 'Access-Control-Allow-Origin' header is present on the requested resource. How can I solve this? Thanks :)
Kevin B is right. It's the Same Origin Policy blocking your request.
What you should do here is to direct your requests from the client to your node server ("/products"). Here, you can easily proxy them to localhost:5500, e.g. using node-http-proxy (https://github.com/nodejitsu/node-http-proxy).
From the node-http-proxy README.md (adapted the host/port to your use-case):
var httpProxy = require('http-proxy');
var proxy = httpProxy.createProxyServer(options);
require('http').createServer(function(req, res) {
proxy.web(req, res, { target: 'http://localhost:5500' });
});
It might be, that this interferes with your current node server (serving you the client-side angular code in the first place). In case you're using Express for this, you can combine "http" and "http-proxy" like this: https://stackoverflow.com/a/22244101/3651406

Resources