HTTP to HTTPS: Express & Heroku - reactjs

Heroku suggests to use NPM packages to handle forcing SSL for express apps. I've tried about a dozen without success. The only thing I can get to work is shown below but then Google gets mad.
Question: Can someone please tell me a working method as of 2022 to force HTTPS on an express app?
<script>
//This works but Google Console gets angry: Page indexing issues: Page with redirect
var host = "www.URL.com" || "URL.com";
if ((host == window.location.host) && (window.location.protocol != "https:")){
window.location.protocol = "https";
}
</script>

If you set the Strict-Transport-Security header, the browser will remember that this is an HTTPS only site after accessing it over HTTPS for the first time. The browser then substitutes every subsequent request to http://your.server with a request to https://your.server, so it will never again visit your site over HTTP.
To ensure that a first visit over HTTPS happens, add a permanent redirect from HTTP to HTTPS. (I don't think this redirect should upset Google, for example, github.com does it in the same way.)
The following code assumes that Heroku runs the app on both HTTP and HTTPS for you.
app.use(function(req, res, next) {
if (!(req.client instanceof tls.TLSSocket))
return res.redirect(301, "https://your.server");
res.set("Strict-Transport-Security", "max-age=86400");
next();
});

Related

Cors issue i also set the proxy in pkg.json due to cors and set the api according to it but this error is not remove

GET http://localhost:3000/api/fetch?search=12312321 404 (Not Found)
cors issue in Reactjs and node js
i use the proxy method in Reactjs but can't get rid from it please help me
proxy
"proxy":"http://localhost:5000/"
tried both
"proxy":"http://localhost:5000"
express
RegisterRoute.route('/fetch/:id').get( ( req , res) =>
{
console.log("called by someone ",req.params.id);
res.send("okey will");
});
Reactjs function which will call the backend api
FetchNotification(){
axios({
'method':'GET',
'url':'api/fetch',
'headers': {
'content-type':'application/octet-stream',
'x-rapidapi-host':'example.com',
'x-rapidapi-key': process.env.RAPIDAPI_KEY
}
,
'params': {
'id':'12312321'
},
})
}
when i simply call axios.get it perfectly work but when i give params to it it gives the error xhr:178 404 not found
a simple server which also returns the same result
const express = require('express');
const cors= require('cors');
const app= express();
var bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({ extended: false }));
// parse application/json
app.use(bodyParser.json());
app.use('*',cors());
app.get('/fetch/:id',(req , res) => {
console.log("calling",req.params.id);
});
app.listen(5000);
I can see that you're using Nodejs as server side. So, you can try changing following line
app.use('*',cors());
to
app.use(cors());
If this doesn't solve the issue, you can try adding a google chrome extension for CORS (Most probably). In this way, you'll not need any proxies being set for running servers.
Also, you need to do a small change in URL, instead of calling
'url':'api/fetch'
you need to provide an id in your call, because the backend api is accepting a parameter
'url':'api/fetch/some_id'
I feel there are multiple issues. I'll try to address them one by one.
Firstly, if you are proxying your requests correctly (which I think you are as per your package.json), then you'd not require the cors package. So you can get rid of that package.
Read more about why you shouldn't let all incoming request bypass the CORS check from security point of view -> Is it safe to enable CORS to * for a public and readonly webservice?
Now secondly, the url which you've specified on the frontend is 'url':'api/fetch', which means browser will make a call to http://localhost:3000/api/fetch?search=12312321 which it correctly did as seen in your error statement for 404.
Specifying the proxy as "proxy":"http://localhost:5000" in package.json means that now you are making requests to http://localhost:5000 instead of http://localhost:3000, but the browser would still think its http://localhost:3000. That's the whole purpose of proxying and how you kinda fool browser to do CORS without throwing any error.
But because on your server, you are listening to app.get('/fetch/:id',(req , res) instead of app.get('api/fetch/:id',(req , res), it doesn't matches with this URL as you have not explicitly handled requests starting with /api in some separate router module either.
So you should either update the url in the axios call to url':'/fetch while the proxy value in package.json is "proxy":"http://localhost:5000" or url':'fetch and "proxy":"http://localhost:5000/" respectively. Notice how i've used the /
OR
You can update the URL on the express end to app.get('api/fetch/:id',(req , res)
Lastly, whenever you receive a request, you need to return some value(string or JSON or even status code) as result.
In your case, you've simply console.log("calling",req.params.id); and didn't send back any response to the UI.
So even if your 404 would resolve after fixing the URL, you'd bump into request timeout error (408) as your UI will keep waiting for a response from the server while you didn't send any.
So you should do something like this maybe:
app.get('api/fetch/:id',(req , res) => {
console.log("calling",req.params.id);
res.send("It worked");
});
Notice how there's a response being sent back -> https://expressjs.com/en/starter/hello-world.html
If you don't want to send back a response, then you can simply return status like 200, 504 etc.
if you apply below code in backend/server i thing it will debug.
app.get('api/fetch/:id',(req , res) => {
console.log("calling",req.params.id);
res.send("It worked");
});

Express JS how to redirect all http traffic to https (Heroku)

I want a mechanism in my Express JS app to redirect all http traffic to https traffic, similar to Rails's force_ssl config. (In Rails's case, it simply sends back a Redirect response to the client
I think the Express server code would go in Server.js, but I need to distinguish these requests from secure (https) requests that should go to the real app.
I think the redirection code looks like this:
var http = express.createServer();
http.get('*', function(req, res) {
res.redirect('https://' + req.headers.host + req.url);
})
// have it listen on 8080
http.listen(80);
You'll note that I can't actually listen for the port by port number, because on Heroku there's the added complication that the app may be redeployed listening on app port of their choosing (it changes every time is redeployed).
so, in essence, I need a way to detect the protocol (http or https) without using the port number here in the Express setup, and then redirect http traffic to https.
The app itself is modeled after "Create React App on Heroku" here https://originmaster.com/running-create-react-app-and-express-crae-on-heroku-c39a39fe7851, so essentially it is a REACT app being served by Express JS on Heroku.
and with example app here https://github.com/Johnnycon/crae-heroku
where I'm stuck is that i've examined the 'process' variable, and while it contains a lot of information, it does not seem to contain any request information, like the protocol or url, as the request comes in.
Any tips or suggestions?
Typically in your server.js near the top, add this function:
// On Heroku, SSL termination happens at the load balancer,
// BEFORE encrypted traffic reaches your node app.
function enforceHttps(req, res, next) {
// Check if directly requested via https
if (req.secure) {
next();
// Heroku sets a header X-Forwarded-Proto to pass the user requested protocol
} else if ((req.headers['x-forwarded-proto'] || '').substring(0, 5) === 'https') {
next();
// Only redirect GET and HEAD requests
} else if (req.method === 'GET' || req.method === 'HEAD') {
const host = req.headers['x-forwarded-host'] || req.headers.host;
// redirect with 301 Moved Permanently instead of default 302
res.redirect(301, `https://${host}${req.originalUrl}`);
} else {
res.status(403).send('This server requires an HTTPS connection.');
}
}
and then later in the file after you define you app, typically as const app = express();:
app.use(enforceHttps);

Unable to call to API after deploying app to Heroku

I've made a weather app that makes an API call to freegeoip to locate your current location's coordinates, and then using those coordinates to connect to openweathermap API to fetch your current location's weather.
In development the app worked perfectly fine. But after deploying to Heroku, I seem to get what looks like a CORS error?
Console logs:
Mixed Content: The page at 'https://weather-react-drhectapus.herokuapp.com/' was loaded over HTTPS, but requested an insecure XMLHttpRequest endpoint 'http://freegeoip.net/json/'. This request has been blocked; the content must be served over HTTPS.
Link to Heroku app
EDIT:
Changing to https seems to work for the freegeoip API (https://freegeoip.net/json/), but doesn't work for the openweathermap API. This is the full console log I get:
GET https://api.openweathermap.org/data/2.5/weather?appid=95108d63b7f0cf597d80c6d17c8010e0&lat=49.25&lon=4.0333 net::ERR_CONNECTION_CLOSED
bundle.js:16 Uncaught (in promise) Error: Network Error
at e.exports (bundle.js:16)
at XMLHttpRequest.d.onerror (bundle.js:16)
Google Maps API warning: NoApiKeys https://developers.google.com/maps/documentation/javascript/error-messages#no-api-keys
Google Maps API error: MissingKeyMapError https://developers.google.com/maps/documentation/javascript/error-messages#missing-key-map-error
Just change API endpoint to use https instead of http.
https://freegeoip.net/json/ works well ;)
Update
Your updated question contains one more request. Unfortunately, api.openweathermap.org is not available over HTTPS. Thus, you need to reach it thru proxy under your control and forward response to your client. For more info, see this answer
If you apply this middleware it should start working correctly
app.use(function (req, res, next) {
if (req.headers['x-forwarded-proto'] === 'https') {
res.redirect('http://' + req.hostname + req.url);
} else {
next();
}
});

Angular $http.post not working, works using postman

I've created an Ionic app which calls an API to post the user's current location.
The request works as follows:
POST: http://mywebsite.com/api/Patients/AddLocation/17
with body:
{
"Latitude": 51.3753786,
"Longitude": -0.0833691
}
However, the following code in my Ionic app does work:
$http.post('http://mywebsite.com/api/Patients/AddLocation/' + $scope.id, data)
.success(function () {
console.log('Updated location');
})
.error(function (error) {
console.log('Error updating location');
console.log("Error: " + error);
});
In which 'data' is the same as the body above.
Any ideas why this isn't working?
UPDATE:
Here's a couple of screenshots of the network request:
Network request available at imgur RWNDF.png
Postman request
It happens if you have not enabled cors in your server.
enable cors in you server.
even if you dont enable cors,
get method will work peerfectly if you have enabled cors using any extension in chrome.
It's because of CORS. Enable cros from the server end and the a header will be set in HTTP Access-Control-Allow-Origin: * .
If your server app is an expressjs app, use below code to enable CORS
var cors = require('cors');
.....
app.use(cors());
Else use chrome extension Allow Cross Origin Request
This should solve the problem
After comparing this to a sister app which uses the same API and works, the only difference I could see was that a few of the plugins were different.
To remedy this I deleted the plugins folder and the platforms folder, re-added android and just the plugins I need, it now works. I guess it's maybe some bug I created by using the wrong mixture of plugins.
A very unsatisfying answer, but all requests work now!

Interesting Puzzle: AngularJS app hangs on http 304

I have an app that is built using: PhoneGap + AngularJS (client) --- ExpressJS + MongoDB (server). The app is hosted on Amazon EC2 on an Ubuntu machine. The app was working totally fine, till...
On the EC2 machine I did a:
sudo apt-get update
After I did this, I started getting the Cross Origin sharing error in my app (looking at the console in Chrome). In my AngularJS app, I have code like so that was throwing the error:
$http.get('http://amazon.ec2.foo/offers').success(function(data) { });
The error was:
XMLHttprequest doesn’t allow Cross-Origin Resource Sharing on http://localhost:8000
So, I updated my server app like so:
app.all("/api/*", function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Cache-Control, Pragma, Origin, Authorization, Content-Type, X-Requested-With");
res.header("Access-Control-Allow-Methods", "GET, PUT, POST, OPTIONS");
return next();
});
This seemed fine at first - I stopped getting the Cross Origin error, and I could see the response from the http request in the console. However, my app would stop responding and seemingly hang immediately after printing the response to the console - I am unable to click on any part of the web page, or open the developer console etc. The only resort is to close the tab and re-open it. Other tabs in Chrome were totally fine.
After debugging this for some more time, I figured that earlier (without those app headers), I was getting a HTTP 200 from the server, but now I'm getting an HTTP 304 (NOT MODIFIED), which it seems that Angular treats as an error.
I found that waiting on the tab for about ~2 minutes results in the web page loading up the data it received from the http request - but the web page stays unresponsive.
I've confirmed a few things to narrow this to AngularJS doing something weird on the HTTP 304:
I'm running this on localhost with the same error - so, it has nothing to do with EC2.
I removed mongodb to just send back some simple data - so, it has nothing to do with Mongo.
I'm running this in Chrome as a simple server - so, I've eliminated PhoneGap to be the issue.
Has anyone seen this before / do you know what the problem may be & how I can solve it?
Thanks!
Angular treats a 304 status code as a failure, to catch the 304 (untested):
var prom = $http.get('http://amazon.ec2.foo/offers')
.catch(function(resp){
if(resp.status !== 304){ $q.reject(resp) }
return resp; // response
})
.catch(function(err){
// handle or throw error
});
// Success responses and 304 responses are now available through the $http promise
prom.then( function(resp){ /* handle 200 and 304 response */ } );

Resources