I'm running a local node js server on my machine and I provide it with a ngrok.io domain.
If I run it local in the web browser with localhost:3000 or with http://1234.ngrok.io everything is ok. If I call it with httpS://1234.ngrok.io I get the error
Mixed Content: The page at 'https://1234.ngrok.io/' was loaded over HTTPS, but requested an insecure XMLHttpRequest endpoint 'http://localhost:3000/calculation'. This request has been blocked; the content must be served over HTTPS.
caused because the route is not http. How can I fix this?
This is the app.js on my node.js
const express = require('express');
const app = express();
const https = require('https');
const http = require('http');
const fs = require('fs');
app.use('/', express.static(__dirname + '/../Client'));
var options = {
key: fs.readFileSync('./https_certificate/client-key.pem'),
cert: fs.readFileSync('./https_certificate/client-cert.pem')
};
app.listen(server_connection.port, () => {
console.log('Server started on port ' + server_connection.port);
//do stuff
});
//load the index.html from the server
app.get('/', function(req, res) {
res.sendFile(path.join(__dirname + '/../Client/index.html'));
});
This is the other file with the route who causes the error:
const express = require('express');
const router = express();
router.post('/calculation', function(req, res) {
//do stuff
}
I already tried to configure the server for https how you can see on the options object in the first file but this resolved in a 502 Bad Gateway.
I also would like to use just https. Is it possible to redirect to https if someone uses http?
It seems that you are sending an ajax request using http to the server, make sure that all your ajax calls are over https to get ride from this error.
Related
I defined my Express js app:
const express = require('express')
var history = require('connect-history-api-fallback');
const path = require('path')
const http = require('http')
const socketio = require('socket.io')
require('./db/mongoose')
const userRouter = require('./routers/user')
const publicDirPath = path.join(__dirname, '../public')
const app = express()
app.use(express.static(publicDirPath))
app.use(history({
index: '../public/index.html'
}))
app.use(express.static(publicDirPath))
app.use(express.json())
app.use(userRouter)
const server = http.createServer(app)
const io = socketio(server)
io.on('connection', socket => {
console.log('New WebSocket connection')
socket.on('join', () => {
socket.emit('message', 'Welcome to the app')
})
})
module.exports = server
Then I use it my index.js file:
const app = require('./app')
const port = process.env.PORT
app.listen(port, () => {
console.log(`Server is up on port ${port}`)
})
When I run the app and send requests from the same port - everything works just fine. But when I try to send requests from different localhost port, ie. 8080, I'm getting cross origin error.
I tried to install cors package and use it as follows:
const cors = require('cors')
app.options('*', cors())
app.use(cors());
And got the same result.
I tried to pass configuration to cors:
app.use(cors({
origin: 'http://localhost:8080'
}));
And still got the same result.
What am I doing wrong and how can I make it work?
When your frontend app tries to make the request to the express server,
The express server is blocking that request because the source of the request (i.e. frontend server) is unknown to the express server
As the request that you are trying to make it out of the domain of the express server. this is the reason where you have to tell the server please accept the request from this origin a.k.a IP Address
and you can achieve the via cors
Let's take a minute to explain what "origin" is in simple words.
When a user browses to a website, he uses an address.
Normally that address is a domain, but when we run our tests we mostly work with local IPs.
For CORS, this doesn't matter. Once you enable Allow-Origins, the server looks at the address the user used to reach the website, and defines it as the "origin" of the request.
Obviously, on a deeper level, everything comes down to IP addresses. But when it comes to CORS, it's more of a high-level security method. It's helps preventing Cross-Site Request-Forgeries for example.
In conclusion, in order for CORS to work,
make sure you allowed the address which the user is using to access the HTTP service.
So, if you're serving a VUE app that's working on http://localhost:8080, and from it calling to an HTTP service on http://localhost:5555, you'll need to do this on the said HTTP service:
app.use(cors({
origin: 'http://localhost:8080'
}));
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.
I have a frontend-only web application on Netlify which has to consume an API on OpenSubtitles.org. Although OpenSubtitles.org enables CORS, sometimes I got preflight errors, so I decided to use a proxy.
I had problems using Netlify's proxy feature, so I decided I will create my own proxy on Heroku, and send my requests from the frontend to there, so these will be proxied to OpenSubtitles.org from a server.
I came up with the following based on the code I found here:
const express = require('express');
const request = require('request');
express()
.use('/', function(req, res) {
req.pipe(
request({
url: 'http://rest.opensubtitles.org/search' + req.url,
headers: {
'User-Agent': 'TemporaryUserAgent'
}
})
).pipe(res);
})
.listen(process.env.PORT || 8000);
I thought I deploy this, try it out, then I will enable CORS on it after that. However I've just realized it is working perfectly without doing anything else. How is it possible? Why can I call this from a frontend-only app on a different domain without explicitly enabling CORS?
Also, what if the server crashes, how to handle the errors there?
CORS is working because the url you're requesting responds with the header Access-Control-Allow-Origin set with a value of *. Since you're piping that response and its headers back to the original res object, it will enable CORS as if it was coming from your local proxy.
Below is a more straightforward example of how to proxy a request to another site and return its response intact using node streams.
const express = require('express')
const request = require('request')
const port = process.env.PORT || 1337
let server = express()
const proxyMiddleware = (req, res, next) => {
let url = `https://www.google.com/${req.url}`
let proxyRequest = request(url)
// Pass request to proxied request url
req.pipe(proxyRequest)
// Respond to the original request with the response from proxyRequest
proxyRequest.pipe(res)
}
server.use(proxyMiddleware)
server.listen(port, () => console.log(`Listening on ${port}`))
How to access website content from another server with Express or HTTP
I have a website that holds all data like template website for example
and I have 3 more websites that get access this website template content HTML CSS everything inside website 2 3 and 4 the only defriend is the route like
mysite.com/template1/user1/index.html
mysite.com/template1/user2/index.html
mysite.com/template1/user3/index.html
I want to have inside website **(n)* only code that gets the HTML CSS and js content from the template server the master how I can do that?.
In PHP is something like
$url = $GET(www.masterserve.com/template1/ + user1 ) echo $url
Any example that I can do the same with node.js and express
// Get dependencies
const express = require('express');
const path = require('path');
const http = require('http');
const bodyParser = require('body-parser');
// Get our API routes
const api = require('./server/routes/api');
const app = express();
// Parsers for POST data
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
// Point static path to dist
app.use(express.static(path.join(__dirname, 'dist'))); <-- idont want static
file only a URL from the master server
// Set our api routes
app.use('/api', api);
// Catch all other routes and return the index file
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, 'dist/index.html'));
});
/**
* Get port from environment and store in Express.
*/
const port = process.env.PORT || '3000';
app.set('port', port);
/**
* Create HTTP server.
*/
const server = http.createServer(app);
/**
* Listen on provided port, on all network interfaces.
*/
server.listen(port, () => console.log(`API running on localhost:${port}`));
If you're trying to get HTTP content from some other server from within your nodejs app, you can use the request module.
request.get('http://somesite.com/template1/user3/index.html', function(err, response, body) {
// access data from other web site here
});
If you're trying to stream that data to some other response, you can also .pipe() the data that you requested to another response. The documentation for that module shows lots of examples of how to do that.
In my scenario I need forward get request to another end point. In my machine there are two servers php and node.js server. Node.js is like a "man in the middle", PHP server must work in the same way.
Node.js server code
var express = require('express');
var fs = require('fs');
var path = require('path');
var http = require('http');
var https = require('https');
var app = express();
var HTTP_PORT = 3000;
// Create an HTTP service
http.createServer(app).listen(HTTP_PORT,function() {
console.log('Listening HTTP on port ' + HTTP_PORT);
});
//endpoint for tracking
app.get('/track', function(req, res) {
sendRequestToOtherEndPoint(req);
processRequest(req);
res.setHeader('Content-Type', 'application/json');
res.send('Req OK');
});
function processRequest(req){
console.log("request processed");
}
function sendRequestToOtherEndPoint(req){
//magic here :)
}
When this server receive a get request in port 3000, it process request information and it must forward the same requesto to another end point.
For example:
Get localhost:3000/track?param1=1¶m2=2
Server process get request
Server forward get request to localhost/final-endpoint?param1=1¶m2=2
Depending on what you're trying to do, you can create a new request to the end-point:
//endpoint for tracking
app.get('/track', function(req, res) {
req.get({url: 'http://end-point', headers: req.headers});
processRequest(req);
res.setHeader('Content-Type', 'application/json');
res.send('Req OK');
});
More info: https://github.com/request/request
There are a couple of useful libraries that one could use:
http-proxy-middleware:
const proxy = require('http-proxy-middleware')
var apiProxy = proxy('/track', {target: 'http://end-point'});
app.use(apiProxy)
axios-express-proxy
import express from 'express';
import { Proxy } from 'axios-express-proxy';
const app = express();
const port = 3000;
app.get('/track', (req, res) => Proxy('http://end-point', req, res));
In you case res.redirect might help.
app.get('/track', function(req, res) {
// process the request
// then redirect
res.redirect('/final-endpoint');
});
Then catch the redirected request in final endpont.
app.get('/final-endpoint', function(req, res) {
// proceess redirected request here.
});
See the Express docs
If your second endpoint is on a different server, (e.g. PHP) then you're going to need to either redirect the client (as in sohel's answer), or spoof a request from Node to the PHP server and then send the response back to the client. This latter option is definitely non-trivial so I would question whether it's crucial not to use a client redirect.
If you're talking about two express endpoints, then I think the simplest answer might be not to actually forward at all, but just use the endpoint callback directly instead:
app.get('/track', trackCallback);
app.get('/otherendpoint', otherendpointCallback);
function otherendpointCallback(req, res) {
// do your thing
}
function trackCallback(req, res) {
otherendpointCallback(req, res);
processRequest(req);
res.setHeader('Content-Type', 'application/json');
res.send('Req OK');
};
Depending on exactly what you want to do at the other end point, you might need to spoof some of req's fields (e.g. req.url)