Nginx Reverse Proxy + ExpressJS + Angular + SSL co

2019-08-22 01:54发布

问题:

I'm new to this stuff and I have a few questions I couldn't answer through Google.

If I get it right - nginx is my webserver - a request to my server is done and nginx serves my client (Angular) but can I also use my Express app to serve it with res.sendFile()? And if I setup nginx with a reverse proxy it works as an instance between my client and backend? So they don't communicate directly with eachother but through nginx and to do so I have to proxy_pass my Express app?

My nginx sites-available config:

# Default server configuration
#
server {
    listen 80 default_server;
    listen [::]:80 default_server;
    server_name blank-agency.org www.blank-agency.org;
    return 301 https://$server_name$request_uri;
} 


server {    
    # SSL configuration
    listen 443 ssl default_server;
    listen [::]:443 ssl default_server;

    ssl on;

ssl_certificate /etc/letsencrypt/live/blank-agency.org/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/blank-agency.org/privkey.pem;

server_name blank-agency.org www.blank-agency.org;  
root /var/www/webserver/public/website;



    location ~ /.well-known {
            allow all;
    }


location / {

    proxy_pass https://localhost:3000;
             proxy_http_version 1.1;
             proxy_set_header Upgrade $http_upgrade;
             proxy_set_header Connection 'upgrade';
             proxy_set_header Host $host;
             proxy_cache_bypass $http_upgrade;

}

As you can see my directory structure is:

/var/www/webserver --> here is my express app.js

/var/www/webserver/public/website --> here are my angular dist files

If I use nginx as a reverse proxy my client is not served anymore (bad gateway 502) so I suppose I have to serve it via my Express app now?

So now my app.js:

// require section

var express = require('express'),
    mysql = require('mysql'),
    bodyParser = require('body-parser'),
    cors = require('cors'),
    path = require('path'),
    app = express();

// middlewares bodyParser + cors
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(cors());



var connection = mysql.createConnection({
    host: '127.0.0.1',
    user: 'root',
    password: 'whatever',
    database: 'whateverDB'
});

connection.connect();


app.use( express.static(__dirname + '/public/website'));

app.get('/', function(req, res){
    res.sendFile(path.join(__dirname + '/public/website/index.html'));
});

app.route('/customers').get((req, res) => {
   connection.query('SELECT * FROM customers', function (err, rows) {
      const results = JSON.stringify(rows);
      console.log(req.param("term"));
      res.send(results);
   })

})



// app listen port

app.listen(3000, function () {
    console.log('Example app listening on port 3000!');
});

Again everything seem to work fine...but why can I access my app through http://blank-agency.org:3000? First of all HTTP requests should be redirected to HTTPS? Same for https://blank-agency/customers --> my data exposed on an URL seems like an security issue haha

I get that it is served on port 3000 cause I sent it through my Express app which listens on port 3000 but how else should it be served? Because nginx with reverse proxy setting gives 502 error like mentioned and i already opened the ports 80 & 443 on my Firewall through iptables on linux.

netstat -tlpn

show nginx on ports 80 and 443

systemctl status nginx

and

nginx -t

says all fine.

I suppose I have a deep misunderstanding of the concepts used here or something else isn't right. Let me know if you need more information.

Thanks in advance :)

回答1:

For the first part:

app.listen(3000, 'localhost', function () {
  console.log('Example app listening on port 3000!');
});

This will prevent access to http://blank-agency.org:3000, because now node with expressjs won't listen on all addresses, but only localhost. (assuming that both nginx and node/expressjs run on same server).

For the second part:

location /customers {
    proxy_pass https://localhost:3000/customers;
         proxy_http_version 1.1;
         proxy_set_header Upgrade $http_upgrade;
         proxy_set_header Connection 'upgrade';
         proxy_set_header Host $host;
         proxy_cache_bypass $http_upgrade;
}

This will reverse proxy only https://blank-agency.org/customers to node/expressjs. In order to reduce configuration burden, better to collect all expressjs URLs under common path like: /api/customers, /api/products, /api/auth etc. This way, you will have to reverse proxy configure only once from /api.

if you require authentication, your angular application and node/expressjs should implement that, then you will have more paths for reverse proxy, like location /api/auth { ... } etc. But that part is application specific, nginx will just handover HTTP requests to node/expressjs, and responses to client. From nginx perspective it should be transparent.



回答2:

For future reference, you can absolutely serve files through both Nginx and express-static.

I have this in my server.js, which serves all files within ./public/ when run locally without Nginx.

app.use('/assets', express.static(path.join(__dirname, 'public')));
app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));

Then in Nginx configuration I have this to serve the same directory. In a production environment, it will be fully handled by Nginx and never hit the Express app for /assets.

location /assets/ {
    alias /home/app/public/;
    sendfile on;
    sendfile_max_chunk 5m;
    tcp_nopush on;
  }

My proxy-pass is a little more complicated due to Prerender.io configuration but you'll want something along these lines. This will send anything except /assets/ to your Express server.

location / {
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_set_header X-NginX-Proxy true;
    proxy_redirect off;
    proxy_pass http://127.0.0.1:5000;
}