Enable Cors on node.js app with nginx proxy - node.js

I have set up a digital ocean droplet that is a reverse proxy server using nginx and node. I used this tutorial from digital ocean as a starting point
https://www.digitalocean.com/community/tutorials/how-to-set-up-a-node-js-application-for-production-on-ubuntu-14-04.
I have also set up ssl with lets encrypt. The issue I am currently having is that I am unable to make cross domain ajax calls to the server. I am getting a error of No 'Access-Control-Allow-Origin' header is present. I have set up the appropriate header response in my node app and have attempted to follow the few examples I could find for nginx with no luck. Below is my code.
nginx with my attempts at headers removed
server {
listen 443 ssl;
server_name lefthookservices.com www.lefthookservices.com;
ssl_certificate /etc/letsencrypt/live/lefthookservices.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/lefthookservices.com/privkey.pem;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_dhparam /etc/ssl/certs/dhparam.pem;
ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-$
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_stapling on;
ssl_stapling_verify on;
add_header Strict-Transport-Security max-age=15768000;
location / {
proxy_pass http://127.0.0.1:8080;
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;
}
location ~ /.well-known {
allow all;
}
}
server {
listen 80;
server_name lefthookservices.com www.lefthookservices.com;
return 301 https://$host$request_uri;
}
Here is my app.js script using express
'use strict';
var colors = require('colors/safe');
var express = require('express');
var knack = require('./knack_call.js');
var bodyParser = require('body-parser');
var cors = require('cors');
colors.setTheme({
custom: ['blue', 'bgWhite']
});
var app = express();
app.use(bodyParser.json());
// allow for cross domain ajax
app.get('/', function(request, response){
response.send('hello\n');
});
app.post('/', function(request, response){
response.header("Access-Control-Allow-Origin", "*");
response.header("Access-Control-Allow-Headers", "X-Requested-With");
response.header("Access-Control-Allow-Methods', 'GET,POST");
knack.getData(request, response);
});
app.listen(8080, '127.0.0.1', function(m){
console.log(colors.custom("Captin the server is at full strength"));
});
Any suggestion that could help me set the correct headers to allow CORS would be greatly appreciated. Thank you in advance.
As a result of Tristans answer below my Nginx code now looks like this.
server {
listen 443 ssl;
server_name lefthookservices.com www.lefthookservices.com;
ssl_certificate /etc/letsencrypt/live/lefthookservices.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/lefthookservices.com/privkey.pem;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_dhparam /etc/ssl/certs/dhparam.pem;
ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES$
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_stapling on;
ssl_stapling_verify on;
add_header Strict-Transport-Security max-age=15768000;
location / {
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
if ($http_origin ~*(https?://.*\exponential.singularityu\.org(:[0-9]+)?$)){
set $cors "1";
}
if ($request_method = 'OPTIONS') {
set $cors "${cors}o";
}
if ($cors = "1") {
more_set_headers 'Access-Control-Allow-Origin: $http_origin';
more_set_headers 'Access-Control-Allow-Credentials: true';
proxy_pass http://127.0.0.1:8080;
}
if ($cors = "1o") {
more_set_headers 'Access-Control-Allow-Origin: $http_origin';
more_set_headers 'Access-Control-Allow-Methods: GET, POST, OPTIONS, PUT, DELETE';
more_set_headers 'Access-Control-Allow-Credentials: true';
more_set_headers 'Access-Control-Allow-Headers: Origin,Content-Type,Accept';
add_header Content-Length 0;
add_header Content-Type text/plain;
return 204;
}
proxy_pass http://127.0.0.1:8080;
}
}
location ~ /.well-known {
allow all;
}
}
Sadly this is still not working.
server {
listen 80;
server_name lefthookservices.com www.lefthookservices.com;
return 301 https://$host$request_uri;
}

It turns out the error message I was getting was inaccurate. The issue was not header setting. It turned out that I needed to make the request with jsonp and I needed to handle the incoming data differently. An error in the function called by app.js was erroring and causing the connection to time out. This resulted in the appropriate headers not being returned to the browser which caused the error message.
For anyone hoping to find an NGINX config that worked this is mine.
proxy_pass http://127.0.0.1:8080;
# proxy_http_version 1.1;
proxy_set_header Access-Control-Allow-Origin *;
# proxy_set_header Upgrade $http_upgrade;
# proxy_set_header Connection '';
proxy_set_header Host $host;
# proxy_cache_bypass $http_upgrade;
Thanks you for the suggestions.

Pull Nginx out of this equation. It doesn't have anything to do with your CORs problem if your setup is as similar to mine as I believe it is. I see that you're using the cors module, but you're not actually using it that I can see.
Your settings are simply enough that you might be able to get away with the defaults so, right below app.use(bodyParser.json());, update your app.js with:
app.use(cors());
That might work right out of the box. If it doesn't, you can pass a set of options. Mine looks something like this:
app.use(cors({
origin: myorigin.tld,
allowedHeaders: [ 'Accept-Version', 'Authorization', 'Credentials', 'Content-Type' ]
}));
Other config options are available in the docs.

You're almost there.
You have to think of the proxy as an external server as well as your Node.js application.
So, in short, you need to add a header to your nginx configuration.
Take a look at this link,
https://gist.github.com/pauloricardomg/7084524
In case this ever gets deleted:
#
# Acts as a nginx HTTPS proxy server
# enabling CORS only to domains matched by regex
# /https?://.*\.mckinsey\.com(:[0-9]+)?)/
#
# Based on:
# * http://blog.themillhousegroup.com/2013/05/nginx-as-cors-enabled-https-proxy.html
# * http://enable-cors.org/server_nginx.html
#
server {
listen 443 default_server ssl;
server_name localhost;
# Fake certs - fine for development purposes :-)
ssl_certificate /etc/ssl/certs/ssl-cert-snakeoil.pem;
ssl_certificate_key /etc/ssl/private/ssl-cert-snakeoil.key;
ssl_session_timeout 5m;
location / {
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# Nginx doesn't support nested If statements, so we
# concatenate compound conditions on the $cors variable
# and process later
# If request comes from allowed subdomain
# (*.mckinsey.com) then we enable CORS
if ($http_origin ~* (https?://.*\.mckinsey\.com(:[0-9]+)?$)) {
set $cors "1";
}
# OPTIONS indicates a CORS pre-flight request
if ($request_method = 'OPTIONS') {
set $cors "${cors}o";
}
# Append CORS headers to any request from
# allowed CORS domain, except OPTIONS
if ($cors = "1") {
more_set_headers 'Access-Control-Allow-Origin: $http_origin';
more_set_headers 'Access-Control-Allow-Credentials: true';
proxy_pass http://serverIP:serverPort;
}
# OPTIONS (pre-flight) request from allowed
# CORS domain. return response directly
if ($cors = "1o") {
more_set_headers 'Access-Control-Allow-Origin: $http_origin';
more_set_headers 'Access-Control-Allow-Methods: GET, POST, OPTIONS, PUT, DELETE';
more_set_headers 'Access-Control-Allow-Credentials: true';
more_set_headers 'Access-Control-Allow-Headers: Origin,Content-Type,Accept';
add_header Content-Length 0;
add_header Content-Type text/plain;
return 204;
}
# Requests from non-allowed CORS domains
proxy_pass http://serverIP:serverPort;
}
}

Related

Express server not working on reverse proxy (NGINX)

I am testing my express server running on proxy. My nginx config file for in sites-available is:
server {
server_name open.yousico.com;
gzip on;
gzip_proxied any;
gzip_types application/javascript application/x-javascript text/css text/javascript;
gzip_comp_level 6;
gzip_buffers 16 8k;
gzip_min_length 256;
location /_next/static/ {
alias /var/www/yousi-website/.next/static/;
expires 365d;
access_log off;
}
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;
}
location /api {
rewrite ^/codify(.*) $1 break;
proxy_pass "http://127.0.0.1:3001";
}
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/open.yousico.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/open.yousico.com/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
server {
if ($host = open.yousico.com) {
return 301 https://$host$request_uri;
} # managed by Certbot
listen 80;
server_name open.yousico.com;
return 404; # managed by Certbot
}
My server file for my express server listening on 3001 is:
const express = require('express');
const app = express();
app.listen(3001, console.log("server is running on 3001"));
app.get('/', (req, res) => {
res.send('API is running');
});
app.get('/hello', (req, res) => {
res.send('Hello World');
});
I tested this express server on my local machine, and it seems to be working fine, but when I deploy it to my cloud server, it displays "Cannot GET /api", is there something wrong with my config file? My understanding is that I set my location to /api and direct it to port 3001, and I'm sure that the server is running on 3001.
"Cannot XXX /YYY" is the Express default response when there is no handler for method XXX for path YYY.
In your case, I don't see an app.get("/api", ...) handler in your server code, so the error is quite warranted.
The rewrite statement in your nginx config,
rewrite ^/codify(.*) $1 break;
doesn't make sense either since there's no way for location /api to match /codify.
If you meant to strip the /api out before the URL is passed to your Express server,
rewrite ^/api(.*) $1 break;
in which case /api would be passed as /, and that would match your app.get("/")...

ERR_CONNECTION_TIMED_OUT when calling backend NodeJS server behind VPN

I just created a website in reactJS (create-react-app) with a login possibility.
I configured autosigned SSL certificates with letsencrypt, added some DNS entries for HSTS and everything that make my website properly traffic-encrypted. My website is running on port 3001 (front-end) and my nodeJs backend is up on port 3000.
Everything works fine, however when some people try to connect to my website behind some VPN (not all of them), they see the page of my app (front-end) but when they try to login (connection to back-end), they get an ERR_CONNECTION_TIMED_OUT.
I cannot reproduce the bug because I do not have such VPN (with my NordVPN its working ok). So I would like you to help me to discover where this problem stems from.
Here is my nginx config file:
# xxx.fr nginx config file
user xxx;
worker_processes 1;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;
events {
worker_connections 1024;
use epoll;
}
http {
upstream frontends {
server xxx.fr:3001;
}
charset utf-8;
include /etc/nginx/mime.types;
default_type application/octet-stream;
access_log /var/log/nginx/access.log;
keepalive_timeout 65;
proxy_read_timeout 200;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
gzip on;
gzip_min_length 1000;
gzip_proxied any;
gzip_types text/html text/css text/xml
application/x-javascript application/xml
application/atom+xml text-javascript;
proxy_next_upstream error;
#include /etc/nginx/sites-enabled/*;
server {
# default_server;
#listen [::]:80;
add_header Strict-Transport-Security 'max-age=31536000; includeSubDomains; preload ';
#add_header Content-Security-Policy "default-src 'self';";
server_name xxx.fr www.xxx.fr;
client_max_body_size 50M;
location ^~ /build/static {
root /home/xxx/x/public;
index index.html;
if ($query_string) {
expires max;
}
}
location = /favicon.ico {
rewrite (.*) /public/favicon.ico;
}
location = robots.txt {
rewrite (.*) /public/robots.txt;
}
location / {
proxy_pass_header Server;
#add_header Strict-Transport-Security "max-age=31536000; includeSubDomains, preload" always;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_redirect off;
proxy_set_header X-Scheme $scheme;
proxy_pass http://frontends;
proxy_ssl_name $host;
proxy_ssl_server_name on;
}
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/xxx.fr/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/xxx.fr/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
server {
if ($host = www.xxx.fr) {
return 301 https://$host$request_uri;
} # managed by Certbot
if ($host = xxx.fr) {
return 301 https://$host$request_uri;
} # managed by Certbot
#listen 80;
server_name xxx.fr www.xxx.fr;
return 404; # managed by Certbot
}
}
And in my nodeJS server (back-end), I have the following header set:
app.use(function (req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Methods", "*");
res.header("Access-Control-Allow-Credentials", true);
// res.header("Access-Control-Allow-Credentials", "true");
res.header(
"Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept, Authorization"
);
next();
});
Can anyone tell me how to fix the ERR_CONNECTION_TIMED_OUT ?
Thank you so much

NodeJS+Express+Nginx - Can't access to domain name without port in URL (404 error)

I have a NODEJS+EXPRESS on my VPS with my domain name but can't access to it.
https://mydomain1.com => 404 error
https://mydomain1.com:8080 => Everything works just fine !
What I want to do : Access to my website without port at url's end like this :
https://mydomain1.com
App folder:
app
├── locales
├── public
│   ├── favicon
│   ├── icon
│   └── image
└── views
└── pages
My server.js NODEJS website :
app.get('/test', (req, response) => {
response.render('pages/test')
})//and a lot of other app.get page
const https = require('https');
var tls = require('tls')
var fs = require('fs')
const SSLoptions = {
key: fs.readFileSync('key.pem'),
cert: fs.readFileSync('cert.pem')
};
https.createServer(SSLoptions, app).listen(8080)
default file on /etc/nginx/site-available :
server {
listen 443 ssl default_server;
listen [::]:443 ssl default_server;
root /var/www/html;
index index.html index.htm index.nginx-debian.html;
server_name mydomain1.com;
location / {
proxy_pass http://127.0.0.1:8080;
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;
proxy_set_header X-NginX-Proxy true;
}
server {
if ($host = mydomain1.com) {
return 301 https://$host$request_uri;
} # managed by Certbot
listen 80;
listen [::]:80 ipv6only=on;
server_name mydomain1.com;
return 404; # managed by Certbot
}
nginf.conf on /etc/nginx/ :
user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
events {
worker_connections 768;
# multi_accept on;
}
http {
##
# Basic Settings
##
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
# server_tokens off;
# server_names_hash_bucket_size 64;
# server_name_in_redirect off;
include /etc/nginx/mime.types;
default_type application/octet-stream;
##
# SSL Settings
##
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
ssl_prefer_server_ciphers on;
##
# Logging Settings
##
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
##
# Virtual Host Configs
##
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
Your nginx.conf is using site-enabled/* and you edited sites-available
Thank you for answer.
I copy/paste /etc/nginx/site-available/default to /etc/nginx/site-enabled/default/ and everything is working !

i can't enable CORS in nginx

I can't Enable CORS on my API Gateway instance, this is how it looks:
Everything is installed on an nginx server under ubuntu 20.04.
React Font-end: https://example.com
-nginx
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
root /home/ubuntu/front;
index index.html index.htm;
location ~* \.(?:manifest|appcache|html?|xml|json)$ {
expires -1;
# access_log logs/static.log; # I don't usually include a static log
}
location ~* \.(?:css|js)$ {
try_files $uri =404;
expires 1y;
access_log off;
add_header Cache-Control "public";
}
# Any route containing a file extension (e.g. /devicesfile.js)
location ~ ^.+\..+$ {
try_files $uri =404;
}
# Any route that doesn't have a file extension (e.g. /devices)
location / {
try_files $uri $uri/ /index.html;
}
return 301 https://example.com$request_uri;
}
server {
listen 443 ssl; # managed by Certbot
listen [::]:443 ssl;
ssl_certificate /etc/letsencrypt/live/xxx.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/xxx.com/privkey.pem; # managed by Certbot
server_name example.com www.example.com;
root /home/ubuntu/front;
index index.html index.htm;
location ~* \.(?:manifest|appcache|html?|xml|json)$ {
expires -1;
# access_log logs/static.log; # I don't usually include a static log
}
location ~* \.(?:css|js)$ {
try_files $uri =404;
expires 1y;
access_log off;
add_header Cache-Control "public";
}
# Any route containing a file extension (e.g. /devicesfile.js)
location ~ ^.+\..+$ {
try_files $uri =404;
}
# Any route that doesn't have a file extension (e.g. /devices)
location / {
try_files $uri $uri/ /index.html;
}
}
express Back-end: https://api.mydomain.com
code add to js
app.use(cors());
´
- nginx
upstream api {
server xx.xx.xx.xx;
}
server {
server_name api.mydomain.com;
location / {
proxy_pass http://127.0.0.1:4001;
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_connect_timeout 30;
proxy_send_timeout 30;
}
listen [::]:443 ssl ipv6only=on; # managed by Certbot
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/xx.xxxx.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/livexx.xxxx.com/privkey.pem; # managed by Certbot
}
minio objectstorage: https://minio.example.com
server {
listen 443 ssl;
server_name minio.example.com;
ssl_certificate /etc/minio/certs/public.crt;
ssl_certificate_key /etc/minio/certs/private.key;
# To allow special characters in headers
ignore_invalid_headers off;
# Allow any size file to be uploaded.
# Set to a value such as 1000m; to restrict file size to a specific value
client_max_body_size 1000m;
# To disable buffering
proxy_buffering off;
location / {
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 Host $http_host;
proxy_connect_timeout 300;
# Default is HTTP/1, keepalive is only enabled in HTTP/1.1
proxy_http_version 1.1;
proxy_set_header Connection "";
chunked_transfer_encoding off;
proxy_pass https://localhost:9000;
# Ajouter les headers de contrôle d'accès CORS
#add_header 'Access-Control-Allow-Origin' '*' always;
#add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
#add_header 'Access-Control-Allow-Headers' 'Origin, X-Requested-With, Content-Type, Accept' always;
#add_header 'Access-Control-Allow-Credentials' 'true' always;
}
i can manage to make http requests from example.com to api.example.com without having cors errors
but when i do an http request fomr example.com to api.example.com then from api.example.com to mini.example.com(or any api), i have cors error " Access to xmlhttprequest at https://api.example.com/upload from https://example.com has been blocked by cors policy: no 'access-control-allow-origin' header is present on the requested ressource
proxy_set_header Access-Control-Allow-Origin "*";
sovled the issue

Error: Request failed with status code 405 , payload return HTML not JSON like intended

I'm setting up a webapps using Node JS + React + NGINX on AWS
and then when i want to access the url /auth
it return me some HTML code instead of JSON like i wanted to be
i tested the code on LOCALHOST and it works fine
I tried to set the folder permission, because i think maybe user permission is the problem
I also tried editing some stuff on the Nginx.conf
below is my app.conf for the nginx
upstream webapp{
server 127.0.0.1:3018;
}
server_names_hash_bucket_size 64;
server_names_hash_max_size 512;
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name 127.0.0.1;
server_name_in_redirect off;
if ($http_x_forwarded_proto = 'http'){
return 301 https://$host$request_uri;
}
location / {
root /home/website/client/build;
try_files $uri /index.html;
log_not_found off;
access_log off;
}
#error_page 405 =200 $uri;
if ( $http_user_agent ~* (nmap|nikto|wikto|sf|sqlmap|bsqlbf|w3af|acunetix|havij|appscan) ) {
return 403;
}
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block;";
add_header Strict-Transport-Security "max-age=2592000; includeSubDomains" always;
}
action {…}
​
payload: Object { isAuth: false, error: true }
​
type: "auth_member"
i expect the output code like this one
but instead the web give me something else
you have to define the upstrem location to be proxed to:
# this is an example you can modify the path on as you get it
location /api {
proxy_pass http://webapp;
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;
}

Resources