Nginx setup for angular app with node API - angularjs

I have been building API using express and front end using angular. Both are in different directory named /var/www/apps/client and /var/www/apps/server.
I want to deploy my application using nginx as a webserver. I am able to host angular application but when trying to request my API getting following error on browser.
Mixed Content: The page at 'https://xxx.xxx.xxx.xxx/' was loaded over HTTPS, but requested an insecure XMLHttpRequest endpoint 'http://xxx.xxx.xxx.xxx:3000/api/videos'. This request has been blocked; the content must be served over HTTPS.
Nginx config
server {
listen 80;
server_name localhost;
access_log /var/log/client/access.log;
error_log /var/log/client/error.log;
root /var/www/apps/client/dist;
charset utf-8;
location / {
rewrite ^ https://$host$request_uri? permanent;
}
}
server {
listen 443 ssl;
server_name localhost;
access_log /var/log/client/access.log;
error_log /var/log/client/error.log;
ssl_certificate /etc/nginx/ssl/nginx.crt;
ssl_certificate_key /etc/nginx/ssl/nginx.key;
keepalive_timeout 5;
root /var/www/apps/client/dist;
charset utf-8;
location ~ ^/(scripts.*js|styles|images) {
gzip_static on;
expires 1y;
add_header Cache-Control public;
add_header ETag "";
break;
}
location / {
try_files $uri /index.html;
}
}
My express server code -
var express = require("express"),
app = express(),
mongoose = require("mongoose"),
config = require("./config");
app.set("appPath", config.root);
mongoose.connect(config.mongo.uri, config.mongo.options);
app.use(function (req, res, next) {
"use strict";
// Website you wish to allow to connect
res.setHeader("Access-Control-Allow-Origin", "*");
// Request methods you wish to allow
res.setHeader("Access-Control-Allow-Methods",
"GET, POST, OPTIONS, PUT, PATCH, DELETE");
// Request headers you wish to allow
res.setHeader("Access-Control-Allow-Headers",
"X-Requested-With,content-type,api_key,adminid,userid");
// Set to true if you need the website to include cookies in the requests sent
// to the API (e.g. in case you use sessions)
res.setHeader("Access-Control-Allow-Credentials", true);
// Pass to next layer of middleware
next();
});
app.use(function (req, res, next) {
"use strict";
next();
});
require("./routes")(app);
app.listen(config.port);
console.log("Listening on port " + config.port)
Serving API at 3000 port. I found this If possible use nginx to redirect by location to node app?
But this is also not working. Any kind of help would be great.

I don't think, the issue is completely with your nginx config. The error message implies that the initial connection from the browser and nginx server was over https. And then you are trying to make an insecure connection through XMLHttpRequest with http.
Solution:
Either load up the initial connection as insecure i.e http
Or make your XMLHttpRequest with a secure connection i.e https.
To help with the config file
# For http
server {
listen 80
server_name localhost
access_log /var/log/client/access.log
error_log /var/log/client/error.log
root /var/www/apps/client/dist
charset utf-8
location / {
proxy_set_header X-Real-IP $remote_addr;
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_pass http://localhost:3000/;
}
}
# For https
server {
listen 443 ssl
server_name localhost
access_log /var/log/client/access.log
error_log /var/log/client/error.log
ssl_certificate /etc/nginx/ssl/nginx.crt;
ssl_certificate_key /etc/nginx/ssl/nginx.key;
root /var/www/apps/client/dist
charset utf-8
location / {
proxy_set_header X-Real-IP $remote_addr;
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_pass http://localhost:3000/;
}
}
Restart Nginx
sudo /etc/init.d/nginx restart

Issue is the other endpoint which you are hitting internally is not Https protected, because of which you are getting the error
http://xxx.xxx.xxx.xxx:3000/api/videos
So, you should write a separate config for client to make the connection https secured. Add that proxy pass also to redirect all your request coming to port 443 to 3000 similar what you did for server
location / {
proxy_set_header X-Real-IP $remote_addr;
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_pass http://localhost:3000/;
}
Make sure ports are different for node and client app

Related

How to call an hls server in a react app that is using https

So, I have a react app that is built and run through a nodejs server. This nodejs server is run with nginx which handles the cert. This react/nodejs server also is running a RTMP server this RTMP server is running an hls server.
So, basically the website has certs and runs over https://. I need to know what approach to take to call the hls server to display a stream on the website. The problem is that the package I am using "node-media-server" doesn't make it obvious how to convert the http to https and so when I cam calling the url from the react app I get a "(blocked:mixed-content)" error. when i am calling the .m3u8 file. So the react app will call the url "http://website.com/live/stream/index.m3u8". Do I need to convert this hls stream to https? If so how do I do this? maybe I need to change my nginx config?
Below is my nginx configuration.
upstream socketio {
# ip_hash;
server 127.0.0.1:8174;
}
upstream reactserve {
server 127.0.0.1:3000;
}
upstream hls {
server 127.0.0.1:8179;
}
#server {
# listen 80;
# server_name www.idealgambler.com;
# rewrite ^(.*) https://$host$1 permanent;
#}
server {
listen 443 ssl;
ssl on;
server_name idealgambler.com www.idealgambler.com;
access_log /var/log/nginx/access-ssl.log;
error_log /var/log/nginx/error-ssl.log;
location / {
# include proxy_params;
proxy_pass http://socketio;
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
proxy_redirect off;
proxy_buffering off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Access-Control-Allow-Origin *;
add_header Front-End-Https on;
add_header Access-Control-Allow-Origin *;
}
location /socket.io/ {
proxy_pass http://socketio;
proxy_redirect off;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Origin *;
}
location /hls {
proxy_pass http://hls;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Origin *;
}
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/idealgambler.com-0001/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/idealgambler.com-0001/privkey.pem; # managed by Certbot
}
Here is the NodeMediaServer code in nodejs:
require("./Casino/CasinoRouters/client-connected")(app, io);
const NodeMediaServer = require("node-media-server");
const config = {
rtmp: {
port: 1935,
chunk_size: 60000,
gop_cache: true,
ping: 60,
ping_timeout: 30,
},
http: {
port: 8179,
mediaroot: "./media",
allow_origin: "*",
},
log_file: "./logs/nms.log",
trans: {
ffmpeg: process.env.FFMPEG_PATH,
tasks: [
{
app: "live",
hls: true,
hlsFlags: "[hls_time=2:hls_list_size=3:hls_flags=delete_segments]",
hlsKeep: true, // to prevent hls file delete after end the stream
dash: true,
dashFlags: "[f=dash:window_size=3:extra_window_size=5]",
dashKeep: true, // to prevent dash file delete after end the stream
},
],
},
};
var nms = new NodeMediaServer(config);
nms.run();
So I have tried to update the nginx file, as well as changing the config for the nodemediaserver to https. The problem with changing http to https in the config is that the key "mediaroot" does not exist in https of nodemediaserver. The error I get is that it can't find the file "./media" when changing it to https in the express app. So basically the https hls will not run only http will run. perhaps there is a different config for running it through https.
I have tried adding the hls to the nginx configuration, but I don't think this did anything.
Ideally you should try to fix the URL and make it match the scheme of application (i.e. never mix http:// with https://) or make the app detect and correct it if the URL comes from an external source.
If it's not doable you might try to fall back to inserting Content-Security-Policy: upgrade-insecure-requests in the HTTP response headers. This would make the browser automatically convert all http:// to https:// as if the latter was originally requested by the app. You seem to have control over the deployment so this can be accomplished within Nginx configuration. Alternatively the header can be added to HTML head, as shown in this answer.
Watch out for WebSockets too. Secure ones (wss://) may require Sec-WebSocket-* headers to be passed by the proxy as per RFC6455. Nginx doesn't do it by default. You will probably want to add the following lines to the configuration:
location /socket.io/ {
# ...
# pass client-to-server Sec-WebSocket-* headers
proxy_set_header Sec-WebSocket-Key $http_sec_websocket_key;
proxy_set_header Sec-WebSocket-Version $http_sec_websocket_version;
proxy_set_header Sec-WebSocket-Extensions $http_sec_websocket_extensions;
proxy_set_header Sec-WebSocket-Protocol $http_sec_websocket_protocol;
}

Request from front end to backend is interupt

I have api (have use load balance). when I send requests from front-end to back-end, then back-end intermittent receive these requests. This request is show "pending" and the end show:
*"Access to XMLHttpRequest at 'https://frontend.domain.com' from origin 'https://backend.domain.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The 'Access-Control-Allow-Origin' header contains multiple values 'https://backend.domain, ', but only one is allowed."
Although, I have already opened cors in back-end. Below is file config nginx of front-end domain.
config nginx
server {
if ($host = frontend.domain.com) {
return 301 https://$host$request_uri;
} # managed by Certbot
if ($host = frontend.domain.com) {
return 301 https://$host$request_uri;
} # managed by Certbot
listen 80;
listen [::]:80;
access_log off;
server_name frontend.domain.com;
client_max_body_size 20M;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl;
server_name frontend.domain.com
client_max_body_size 20M;
ssl off;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
location / {
proxy_pass http://0.0.0.0:3000;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_http_version 1.1;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_method GET;
proxy_connect_timeout 6000;
proxy_send_timeout 6000;
proxy_read_timeout 6000;
send_timeout 6000;
client_max_body_size 20M;
}
ssl_certificate /etc/letsencrypt/live/domain.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/domain.com/privkey.pem; # managed by Certbot
}
And addition before I no have, suddenly after I restart the server I have this error.
Any tip to this error goes away. Thanks.

AWS front-end back-end communication

I have 2 apps running on ASW Symfony on port 8000 (local) and react 3000(local) but accessible through TCP on port 80 redirections was achieved by listening of port 80 within nginx server.
server {
listen 80;
server_name example.info www.example.info;
location / {
proxy_pass http://127.0.0.1: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;
proxy_redirect off;
}
}
server {
listen 8000;
server_name example.info www.example.info;
location / {
proxy_pass http://127.0.0.1:8000;
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;
proxy_redirect off;
}
}
I have tried to listen and redirect of two ports but without success.
Within the server, Symfony application is accessible with curl http://127.0.0.1:8000
From outside in my react app I am sending api requests to asw.external.ip (123.123.123.123:800) but I get timeout. How could I access my back-end from outside?
AWS ElasticBeanstalk - Configuring the Proxy Server to your back-end
You can use this config file to your Aws Ec2 as well.
/etc/nginx/conf.d/proxy.conf
upstream nodejs {
server 127.0.0.1:5000;
keepalive 256;
}
server {
listen 8080;
access_log /var/log/nginx/access.log main;
location / {
proxy_pass http://nodejs;
proxy_set_header Connection "";
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
gzip on;
gzip_comp_level 4;
gzip_types text/html text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
## Optional configuration if you want to allow AWS
## to cache your static files
location /static {
alias /var/app/current/static;
}
}
Edit - Configuring Nginx for Symfony
server {
listen 8080;
server_name sf2testproject.dev;
root /home/maurits/public_html/web;
location / {
# try to serve file directly, fallback to rewrite
try_files $uri #rewriteapp;
}
location #rewriteapp {
# rewrite all to app.php
rewrite ^(.*)$ /app.php/$1 last;
}
location ~ ^/(app|app_dev|config)\.php(/|$) {
fastcgi_pass 127.0.0.1:9000;
fastcgi_split_path_info ^(.+\.php)(/.*)$;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param HTTPS off;
}
}
Where:
listen is the port that your application communicate with the world.
fastcgi_pass is a binary protocol for interfacing interactive programs with a web server
References:
Aws ElasticBeanstalk - Nodejs platform proxy
Symfony Hhvm 3 nginx 1.4 vs PHP 5.5 apache 2.4
FastCGI Oficial Example

nginx loopback reverse proxy can't access custom paths

Im running this loopback app in my server and trying to access it through an nginx reverse proxy, but can't seem to get nginx configured correctly, I'm a total noob to nginx. Here's my config file /etc/nginx/sites-available/example.com
server {
listen 80;
server_name www.example.com;
server_name example.com;
server_name _;
root /apps/example/client;
location ~ ^/.well-known {
allow all;
}
location / {
return 301 https://example.com;
}
}
server {
server_name example.com;
server_name www.example.com;
#listen 80;
# SSL configuration
listen 443 ssl ;
ssl_certificate /etc/letsencrypt/live/www.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/www.example.com/privkey.pem;
root /apps/example/client;
# Check Alive
location ~ ^/.well-known {
allow all;
}
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $http_host;
proxy_bind 127.0.0.1;
proxy_pass http://127.0.0.1:3000/;
}
location /api {
proxy_pass http://127.0.0.1:3000/api;
}
location /explorer {
proxy_pass http://127.0.0.1:3000/explorer;
}
}
and my loopback server serves static files like this:
app.use(loopback.static(path.resolve(__dirname, '../client')));
and at the end of my server.js file I serve the index.html file for any path not processed so angular processes that path in the front end
function startsWith(string, array) {
for(let i = 0; i < array.length; i++)
if(string.startsWith(array[i]))
return true;
return false;
}
let ignoredPaths = ['/css', '/fonts', '/img', '/js', '/lib', '/sfx', '/views', '/api'];
app.all('/*', function(req, res, next) {
//Redirecting to index only the requests that do not start with ignored paths
console.log("Requested url: ",req.url);
if(!startsWith(req.url, ignoredPaths)) {
res.sendFile('index.html', {root: path.resolve(__dirname, '../', 'client')});
} else {
next();
}
});
The issue is that I can only access http://www.example.com/ and it takes a lot to load and the url flashes many times like it's redirecting a lot of times, I redirect in front-end with angular to /home, I have the angular routing to use html5 to remove the /#!/ from the path, but I always get a Bad Gateway error if I try to access any other path, say http://www.example.com/admin/login and it's a nginx error, it's not reaching loopback so it can serve the index file. I've tried the try_files $uri $uri/ /index.html; but if I try to access any other custom path, say /auth/facebook it forces it to load it through index.html and that's a custom path I have setup on loopback to do special behaviour. I have no idea how to solve this, I just used that template config file, all I need is for it to redirect all requests from example.com to my app in localhost:3000 and do the same in the opposite direction.
So i kept trying, and tried again using the try_files with a #path (I guess that's kind of a label or something) and what I was missing when I tried it before was to attach $uri at the end of the proxy_pass
It ended up like this:
location / {
try_files $uri $uri/ #proxy;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $http_host;
proxy_bind 127.0.0.1;
proxy_pass http://127.0.0.1:3000/;
}
location #proxy {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $http_host;
proxy_bind 127.0.0.1;
proxy_pass http://127.0.0.1:3000$uri;
}
And all works dandy now :P.

How to handle redirects in Express application with Reverse Proxy

I have two Express powered NodeJS applications that are being served by Nginx. One application is a RESTful API, that is consumed by an Angular SPA and the other one is an Admin portal. This is something I'm trying to achieve here:
location / {
# Serves Client Side Angular Application
}
location /api {
# Serves RESTful Application
}
location /admin {
# Serves Admin Portal
}
Here is my complete configuration for nginx:
server {
server_name localhost;
listen 80;
listen [::]:80;
index index.html;
location / {
expires -1;
add_header Pragma "no-cache";
add_header Cache-Control "no-store, no-cache, must-revalidate, post-check=0, pre-check=0";
root /var/www/example/client/dist;
try_files $uri $uri/ /index.html =404;
}
location /admin/ {
proxy_pass http://127.0.0.1:3010/;
proxy_http_version 1.1;
rewrite /admin/(.*) /$1 break;
proxy_redirect off;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host/admin/;
proxy_set_header X-NginX-Proxy true;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
location /api {
proxy_pass http://127.0.0.1:3011;
proxy_http_version 1.1;
proxy_set_header X-Real-IP $remote_addr;
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_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
Both of the NodeJS application utilizes forever and run on their defined ports, perfectly.
The problem I'm facing with my admin application, when I try to hit http://example.com/admin it redirects me to http://example.com/login when it should redirect me to http://example.com/admin/login. I tried to solve this issue by adding a middleware to the admin application, like:
app.use('*', function (req, res, next) {
if(process.env.target === 'dev'){
if(req.originalUrl.indexOf('/admin') > -1){
res.redirect('/admin' + req.url);
}
next();
}
});
But this doesn't work. How should I handle this situation? Is there anyway nginx can handle application redirects? If not, how should I handle it in application for different environments(Dev, Staging, Prod etc)? I'm using nginx 1.4.6, node 0.12.2 and express 4.12.2. I have no issue if I have to add any 3rd party module in my application but I'd prefer if there is an nginx solution for my problem.
Thanks.
The problem with your nginx config is the rewrite for the /admin route:
rewrite /admin/(.*) /$1 break;
With this rule in place your admin portal node app doesn't know that its paths are all hosted off of a /admin root, so if your app does a redirect relative what it thinks is root, eg res.redirect('/login'), it makes sense that it would send the browser to localhost/login.
An alternative would be to remove your nginx rewrite and just make the admin portal aware of this /admin path. That way you have the option of redirecting to a relative path, like this: res.redirect('login') which tells express to take the current request's path into account. You can use the express.Router to make this cleaner, like this for example:
var adminRoutes = express.Router()
.get('/', function(req, res) {
if(!req.user) {
// Will redirect to /admin/login
res.redirect('login');
} else {
res.send('Hello');
}
})
.get('/login', login);
// Tell express to host all routes above under a common /admin root
app.use('/admin', adminRoutes);
If on the other hand there's a better reason why you must have the nginx rewrite, you can always just include the /admin root manually for all of your app's redirects. You could provisionally append this mount path by reading it from a custom header set byproxy_set_header. For example:
app.use(function(req, res, next) {
res.mountedAt = req.headers['X-Mounted-At'];
next();
});
app.get('/', function(req, res) {
res.redirect(path.join(res.mountedAt, '/login'));
});
I solved this by combining Andrew Lavers's first solution with
location /admin/ {
proxy_pass https://127.0.0.1:3010/admin/;
}

Resources