nginx serve static file in response to POST request - node.js

I have nginx setup as reverse-proxy for node.js. For file uploads I'm asking NGINX to forward the request to node.js but for file download, I want that to be done by NGINX. I can get it to work using a GET request.
But I'd really like to authenticate the session via POST request and then serve the file. Please advise on how to achieve this.
Here is my "default" config file for NGINX located under /etc/nginx/sites-available :
server {
listen 3000;
server_name X.Y.Z;
root /mnt/Files/;
error_log /home/nginx/logs/error.log debug;
access_log /home/nginx/logs/access.log;
client_max_body_size 20M;
location /download/* {
root /mnt/Files/;
access_log on;
autoindex on;
set $var1 = $1
try_files $uri $uri/ =404;
}
location /upload {
proxy_pass http://127.0.0.1:3001;
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;
}
}
Example form I will submit will have the following fields:
username: user1
password: pass1
subFolder:
file:

You can not put so much logic inside nginx config file.
first you need to retrieve post parameters.
then you need to validate that against some DB like mysql or mongoDB.
and then return 401( un-authorised).
I don't think nginx can do all this.
I suggest do all this as part of express static feature.
var express = require('express');
var app = express();
var auth = express.basicAuth(function(user,password) {
return 'john' === user && '1234' === password;
});
app.use('/media', auth);//should be before static
app.use('/media', express.static(__dirname + '/media'));

Related

How can I get visitors IP by Node.js?

I have my own VPS server and I have script in Node.js which display the visitor's IP but always when I visit website I get in console local IP address (127.0.0.1). I use Nginx.
Any idea?
Node.js script:
#!/usr/bin/env nodejs
const http = require('http');
const host = '127.0.0.1';
const port = 8080;
const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
var ip = (req.headers['x-forwarded-for'] || '').split(',').pop().trim() ||
req.connection.remoteAddress || req.socket.remoteAddress || req.connection.socket.remoteAddress;
console.log(`IP = ${ip}`);
});
server.listen(port, host);
Nginx proxy/headers configuration:
server {
listen 80 default_server;
listen [::]:80 default_server;
root /var/www/html;
index index.html index.htm index.nginx-debian.html;
server_name _;
location / {
try_files $uri $uri/ =404;
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;
}
}
You need to rig your nginx reverse proxy to pass along the requester's IP address. Adding these two settings to nginx.conf does the trick.
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
Put these lines in your location{...} stanza of nginx.conf along with your proxy-pass and the rest.
With these changes, nginx inserts two http headers into each request: X-Forwarded-For and X-Real-IP. (There's a new standard Forwarded: header, but nginx doesn't handle it easily as of mid-2020.)
Then, use app.set() to add proxy server support to your nodejs program to interpret those headers. Put this line in your www or http-server.js Javascript program shortly after your const app = express() line.
app.set( 'trust proxy', 'loopback' )
Express will then muck around with the X-Forwarded-For header for you and put the appropriate IP address in req.ip.
I've linked to some documentation. You would be wise to read it.

Port numbers not hiding in nginx reverse proxy (next js server)

I am trying to deploy a next-js app by create-next-app, I have a custom express server like this -
const express = require('express')
const next = require('next')
const dev = process.env.NODE_ENV !== 'production'
const nextApp = next({ dev })
const handle = nextApp.getRequestHandler()
const fs = require('fs')
nextApp.prepare()
.then(() => {
const server = express ()
let port = 3000;
let options = {
key: fs.readFileSync('some key..', 'utf-8'),
cert: fs.readFileSync('some cert..', 'utf-8'),
};
server.get(
...
)
let app = https.createServer(options, server)
.listen((port), function(){
console.log("Express server listening on port " + port);
});
})
.catch((ex) => {
console.error(ex.stack)
process.exit(1)
})
I want to deploy this as the website when someone types the URL subdomain.maindomain.com so I saved two nginx configuration files like this -
/etc/nginx/sites-available/default AND /etc/nginx/sites-available/subdomain.maindomain.com
the default file contains this
server {
root /var/www/html;
index index.html index.htm index.nginx-debian.html;
server_name maindomain.com www.maindomain.com;
location / {
# try_files $uri $uri/ =404;
proxy_pass http://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;
}
listen [::]:443 ssl ipv6only=on; # managed by Certbot
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/maindomain.com/fullchain.pem;$
ssl_certificate_key /etc/letsencrypt/live/maindomain.com/privkey.pe$
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
and the subdomain.maindomain.com file looks like this
server {
if ($host = www.subdomain.maindomain.com) {
return 301 https://$host$request_uri;
} # managed by Certbot
if ($host = subdomain.maindomain.com) {
return 301 https://$host$request_uri;
} # managed by Certbot
listen 80;
listen [::]:80;
root /var/www/subdomain.maindomain.com/somecodefolder/;
index index.html index.htm index.nginx-debian.html;
server_name subdomain.maindomain.com www.subdomain.maindomain.com;
location / {
proxy_pass http://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;
# try_files $uri $uri/ =404;
}
}
if I'm typing https://subdomain.maindomain.com:3000, everything works fine, I see my website running. But when I type https://subdomain.maindomain.com (without the port number) it shows nothing. How can I get the content I want when I type just the url without the port number. I have tried many combinations, but could'nt do. someone please help i've been trying since 2 days.
Try with other applications in order to validate if something is wrong in your application.
Configure nginx to use domain instead ports are not complex. Just add https configurations but the main configurations will be the same.
Steps
npm install
node main_domain.js
node subdomain.js
Check if webs are working:
Add the following lines to your /etc/hosts. This will help us to use domains without enterprise web hosting company register.
127.0.0.1 maindomain.com
127.0.0.1 subdomain.maindomain.com
Create a file in /etc/nginx/conf.d called maindomain.com.conf or whatever you want but with .conf
server {
listen 80;
server_name maindomain.com;
location / {
proxy_pass http://localhost:3000/;
}
}
Create a file in /etc/nginx/conf.d called conf.d/subdomain.maindomain.com.conf or whatever you want but with .conf
server {
listen 80;
server_name subdomain.maindomain.com;
location / {
proxy_pass http://localhost:3001/;
}
}
Restart the nginx
service nginx restart
And now, you could use a domain instead ip:port
Try to change from
proxy_pass http://localhost:3000;
Into
proxy_pass http://127.0.0.1:3000;

Express/Nginx can't load static files

My Express app can't load static files on browser but it works in curl. I use nginx as webserver. App works well but without any static files. What am I doing wrong here?
App.js
...
App.use(cors())
App.use("/data", express.static(__dirname + '/data'));
App.use('/api', require('./routes/api'))
App.listen(1337)
nginx
server
{
listen x.x.x.x:80;
server_name example.com www.example.com ;
location /api {
proxy_pass http://x.x.x.x:1337;
...
}
location / {
return 301 https://$server_name$request_uri;
}
}
server
{
listen x.x.x.x:443 ssl;
server_name example.com www.example.com ;
root /path/to/public_html;
index index.php index.html index.htm;
ssl on;
location /api {
add_header X-Cache "HIT from Backend";
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_pass http://x.x.x.x:1337;
...
}
}
Curl is ok:
http://127.0.0.1:1337/data/pic.png
But browser is not:
http://example.com/api/data/pic.png
router:
App.use('/api', require('./routes/api'))
Try this Nginx config, it will redirect http://example.com/api/data/pic.png to http://x.x.x.x:1337/data/pic.png
location /api {
rewrite /api(.*) /$1 break;
proxy_pass http://x.x.x.x:1337;
...
}

Prerender with nginx and node.js returns 504

If I understand things correctly I can setup nginx in a way that it handles crawlers (instead of nodejs doing it). So I removed app.use(require('prerender-node').set('prerenderToken', 'token')) from express configuration and made the following nginx setup (I do not use prerender token):
# Proxy / load balance (if more than one node.js server used) traffic to our node.js instances
upstream my_server_upstream {
server 127.0.0.1:9000;
keepalive 64;
}
server {
listen 80;
server_name test.local.io;
access_log /var/log/nginx/test_access.log;
error_log /var/log/nginx/test_error.log;
root /var/www/client;
# Static content
location ~ ^/(components/|app/|bower_components/|assets/|robots.txt|humans.txt|favicon.ico) {
root /;
try_files /var/www/.tmp$uri /var/www/client$uri =404;
access_log off;
sendfile off;
}
# Route traffic to node.js for specific route: e.g. /socket.io-client
location ~ ^/(api/|user/|en/user/|ru/user/|auth/|socket.io-client/|sitemap.xml) {
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 X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true;
proxy_set_header Connection "";
proxy_http_version 1.1;
proxy_pass_header X-CSRFToken;
sendfile off;
# Tells nginx to use the upstream server
proxy_pass http://my_server_upstream;
}
location / {
root /var/www/client;
index index.html;
try_files $uri #prerender;
access_log off;
sendfile off;
}
location #prerender {
set $prerender 0;
if ($http_user_agent ~* "baiduspider|twitterbot|facebookexternalhit|rogerbot|linkedinbot|embedly|quora link preview|showyoubot|outbrain|pinterest|slackbot|vkShare|W3C_Validator") {
set $prerender 1;
}
if ($args ~ "_escaped_fragment_") {
set $prerender 1;
}
if ($http_user_agent ~ "Prerender") {
set $prerender 0;
}
#resolve using Google's DNS server to force DNS resolution and prevent caching of IPs
resolver 8.8.8.8;
if ($prerender = 1) {
#setting prerender as a variable forces DNS resolution since nginx caches IPs and doesnt play well with load balancing
set $prerender "127.0.0.1:3000";
rewrite .* /$scheme://$host$request_uri? break;
proxy_pass http://$prerender;
}
if ($prerender = 0) {
rewrite .* /index.html$is_args$args break;
}
}
}
But when I test it by curl test.local.io?_escaped_fragment_= I get got 504 in 344ms for http://test.local.io
Node version is 6.9.1. I use vagrant to setup environment.
The above configuration works fine. All it was missing is an entry in /etc/hosts : 127.0.0.1 test.local.io

Serve root static file with nginx as node reverse proxy

I have a nodejs server that's served with nginx as reverse proxy. That part is ok, and static files locations are set up correctly. But I want the root address to serve a static html file, and I don't know how to configure nginx so that the root url is not redirectected to the node app. Here's my server block:
upstream promotionEngine {
server 127.0.0.1:3001;
}
server {
listen 3000;
server_name localhost;
root C:/swaven/dev/b2b.pe/promotionEngine/templates/;
index index.html;
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://promotionEngine;
proxy_redirect off;
}
location /public/ {
alias C:/swaven/dev/b2b.pe/promotionEngine/public/;
}
location /assets/ {
alias C:/swaven/dev/b2b.pe/promotionEngine/assets/;
}
}
htttp://localhost:3000/ping and http://localhost:3000/public/js/riot.js are correctly served.
But http://localhost:3000 keeps being sent to the node server, where I would like it to return a static index.html. If I remove the / location bloc, the html file is correctly served. How would I configure the location to work as reverse proxy for all urls except the root one ?
UPDATED: (based on comments and discussion)
You'll need 2 exact location blocks. One to intercept the / location and another to serve just /index.html.
An exact location block is described on nginx docs:
Also, using the “=” modifier it is possible to define an exact match of URI and location. If an exact match is found, the search terminates.
Simply using the index directive does not work. Because nginx creates an internal redirect to allow other blocks to match index.html. Which gets picked up by your proxy block.
upstream promotionEngine {
server 127.0.0.1:3001;
}
server {
listen 3000;
server_name localhost;
# Do an exact match on / and rewrite to /index.html
location = / {
rewrite ^$ index.html;
}
# Do an exact match on index.html to serve just that file
location = /index.html {
root C:/swaven/dev/b2b.pe/promotionEngine/templates/;
}
# Everything else will be served here
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://promotionEngine;
proxy_redirect off;
}
location /public/ {
alias C:/swaven/dev/b2b.pe/promotionEngine/public/;
}
location /assets/ {
alias C:/swaven/dev/b2b.pe/promotionEngine/assets/;
}
}
You can use =/ this type of location have higher priority due to lookup:
location =/ {
root ...
}
This request will not even try to reach other locations.
Something like this, adjust for your own use case.
http {
map $request_uri $requri {
default 1;
/ 0;
}
...........
server {
listen 80;
server_name www.mydomain.eu;
root '/webroot/www.mydomain.eu’;
if ($requri) { return 301 https://www.mydomain.eu$request_uri; }
location / {
..........
}
}

Resources