I have on a production server an Angular app (using Universal for server-side rendering) running on Node Express localhost:4000, and I configured Nginx reverse proxy for the app. The production server is using HTTPS for its domain name.
Here is nginx config in /etc/config/sites-enabled:
location ~ (/landing/home|/landing/company|/landing/contact|/landing/support) {
proxy_pass http://127.0.0.1:4000;
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_cache_bypass $http_upgrade;
add_header X-Cache-Status $upstream_cache_status;
}
location / {
proxy_pass http://127.0.0.1:5000;
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;
}
Here is nginx.conf
user ubuntu;
worker_processes auto;
pid /run/nginx.pid;
events {
worker_connections 2048;
multi_accept on;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
##
# 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;
##
# Gzip Settings
##
gzip on;
gzip_disable "msie6";
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
##
# Virtual Host Configs
##
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
In Chrome Dev Tools - Network, here is sample request & response for an image (SVG file). The image that nginx sent is older version, and has since updated (file name unchanged) on the Angular side. Please note that this file is just a sample, the issue I'm facing is not just this one file, but all static files (including css and js files).
request header
response header
To verify, I did curl on a client and on the server, to the same image file. Here are result of curl:
curl result from a client browser, result was from nginx
curl result on the server, comparing between curl to localhost:4000 and curl to the actual public url
We can see that in response from localhost:4000 it is the latest version of the image, where in response from the public url it is older version of the same image.
I checked in /etc/nginx, there is no chache folder in there. I thought about clearing nginx's cache, but I couldn't find it there.
I have tried adding many things in config, including:
add_header Last-Modified $date_gmt;
add_header Cache-Control 'no-store, no-cache';
if_modified_since off;
expires off;
etag off;
and
proxy_cache off;
And somehow even the X-Cache-Status doesn't show up in response header neither, but comparing the curl result from localhost and the public url, it is clear to me that it must be something to do with nginx.
Anyone have suggestion on what to do to make nginx sends response from the actual output of localhost:4000, instead of from cache? Thank you.
UPDATE #1:
Sorry I only included partial nginx conf. I have found the root cause: I actually have two Node Express running on the same domain, one is on port 4000 (Angular Universal) and the other is on port 5000 (non-Universal). I have updated the excerpt of nginx conf above to include the other location directive for the one on port 5000. Please see my answer below for further explanation of what I did wrong to cause the problem in this question.
I found out the root cause of the problem.
I actually have two Node Express running on the same server, same domain. One in on port 4000 (uses Angular Universal), and the other on port 5000 (non-Universal). I have edited my question to include the second location directive in nginx conf.
The way I had my nginx conf made it looked like the whole page came as response from localhost:4000, but some parts within the page (images, style sheets, etc) actually were response from localhost:5000, due to the url of the request did not match the pattern in nginx conf for localhost:4000. So localhost:5000 got to respond the the request, and the files that localhost:5000 had was older version (not all, but the one I tested with curl happened to be older version).
I only realized this situation when I disabled the second location directive in the nginx conf, effectively disabled localhost:5000 from responding to any request, and then I saw many 404 errors because of that.
To solve this problem, meaning to have both localhost:4000 and localhost:5000 active, and still getting the correct respond, I had to make some adjustments to the routings in my Angular code.
Related
I'm trying to create a basic api which does stuff, as an api does, however it is sitting behind both an Nginx instance and a Cloudflare layer for security, however every time I make a request all the headers go through find but the body of the request (application/json) seems to be getting removed.
I have tried logging it on the nginx instance and I just get '-' every request so I think it could be Cloudflare. I have tested locally and I am definitely able to receive the body as it is. I've looked through the req object and there is no body anywhere, all the auth headers are fine just the body.
EDIT (in response to AD7six): Sorry i'll clear my question up, i'm saying that both the access log is missing the body and that my code behind the proxy does not receive it. I'll attach the nginx config / log now.
On further inspection my nginx config is listening to port 80 however all the responses are going to https... I hope that makes sense.
NGINX Config
user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
events {
worker_connections 768;
# multi_accept on;
}
http {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
log_format postdata $request_body;
##
# 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;
##
# Gzip Settings
##
gzip on;
# gzip_vary on;
# gzip_proxied any;
# gzip_comp_level 6;
# gzip_buffers 16 8k;
# gzip_http_version 1.1;
# gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
##
# Virtual Host Configs
##
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
server {
listen 80;
listen [::]:80;
server_name dev.ru-pirie.com;
location / {
access_log /var/log/nginx/postdata.log postdata;
proxy_pass http://192.168.1.74:3000;
}
}
}
All the log contains is - per request
When requests are proxied via Cloudflare, by default they are modified with additional headers, for example CF-Connecting-IP that shows the IP of the original client that has sent the request (full list here).
There are other features that Cloudflare users can implement that may alter the request, but only when explicitly configured to do so: for example, someone could write a Cloudflare Worker that modifies arbitrarily the incoming request before forwarding it to the origin server. Other general HTTP request changes are possible using Cloudflare Rules.
Cloudflare would not alter the body of an incoming request before passing it to the origin, unless explicitly configured to do so for example with Workers.
We are using a VM running a nginx proxy ( caching turned off ) to redirect hosts to the correct CDN url.
We are experiencing issues with the proxy showing old content that does not match what shows on the CDN. The CDN provider we use is Verizon (by through Azure - Microsoft CDN by Verizon).
When we do updates to the origin we automatically send purge requests to the CDN. These can be both manual and automatic updates dynamically and both single url purges and wilcard ones. What seems to be happening is when we get 2 pruge requests close in time. The first one goes through to the proxy, but the second one does not. Although both show correctly when accessing the CDN url directly.
To mention is that this issue only happens about 30% of the time.
nginx samle conf:
server {
resolver 8.8.8.8;
server_name <CUSTOM HOST>;
location / {
# Turn off all caching
proxy_no_cache 1;
proxy_cache_bypass 1;
proxy_redirect off;
expires -1;
proxy_cache off;
# Proxy settings
proxy_set_header X-Forwarded-Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_headers_hash_max_size 512;
proxy_headers_hash_bucket_size 128;
# Override cache headers to set 2min browser cache
proxy_hide_header Cache-Control;
add_header Cache-Control max-age=120;
proxy_pass "<CDN URL>request_uri";
}
}
nginx.conf:
user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
events {
worker_connections 768;
# multi_accept on;
}
http {
sendfile off;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
include /etc/nginx/mime.types;
default_type application/octet-stream;
##
# SSL Settings
##
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
gzip on;
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
Below is an example where the CDN is showing newer content then the proxy, we have a Last-Modifed mismatch:
CDN:
PROXY:
I have tried through a VPN to see if there is anything whit a particular POP that the proxy hits, but all POP:s show the correct content.
When the error is present, sending a curl request from the proxy to the CDN will result in the same incorrect headers.
After we perform a purge several requests goes through to origin directly until the CDN starts serving a Cached version again.
The we receive the first HIT about 1 min later.
I started to assume that this might have something internally to do with Azure & Verizon.
So I created an exact duplicate proxy hosted on amazon, but the error seem to precist.
Is there something else in the nginx that can cause this behavior?
Try adding just to see if the pages are retrieved each hit.
add_header Last-Modified $date_gmt;
add_header Cache-Control 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0';
if_modified_since off;
expires off;
etag off;
I have setup an NGINX reverse proxy to a web service (www.aaa.com) through another domain name (www.bbb.com) while adding a few additional pages to the latter.
Request come from www.bbb.com (nodejs app) but they need to look like they are coming from www.aaa.com or I will have a CORS (Cross-origin resource sharing) issue. Hence NGINX to tweak the headers.
NGINX config
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
include servers/*;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 443 ssl;
server_name www.bbb.com;
ssl_certificate /etc/pki/nginx/server.crt;
ssl_certificate_key /etc/pki/nginx/private/server.key;
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 5m;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
location / {
proxy_set_header X-Real-IP 127.0.0.1;
proxy_set_header Host www.aaa.com;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_pass https://www.aaa.com;
proxy_http_version 1.1;
}
}
}
Looks like one of the resources doesn't get the correct MIME Type through the reverse proxy
https://www.aaa.com/scripts/some.script.api.js
Error
Refused to execute script from 'https://www.bbb.com/scripts/some.script.api.js' because its MIME type ('text/html') is not executable, and strict MIME type checking is enabled.
text/html is incorrect indeed, I would be expecting application/javascript
1. Why is this happening?
I figured that MIME Types are just set automatically? Could that be that www.aaa.com has implement some new CORS security rule?
2. Is there a way to tell NGINX to set the correct MIME Type for that particular file? I have tried a few things to no avail.
e.g.
add_header Content-Type application/javascript;
but I may have used wrong.
Any pointers?
Similar issue described on another Stackoverflow question
You could try using http://nginx.org/r/default_type; another thing that comes to mind would be http://nginx.org/r/proxy_hide_headers.
I would also suggest that the first line of action would be to determine the root cause of this issue — is the incorrect MIME type coming from your upstream? Then look there.
Otherwise, my guess would be that the reason you're getting text/html is because something else is wrong with your configuration, and a 404 Not Found response gets generated for your .js file (or maybe even 403 Forbidden by the upstream, or 500 by nginx), thus resulting in the text/html MIME type.
I just setup nginx + gunicorn to serve up a Pyramid web application. My application relies on getting the subdomain, which is different for every client. I didn't know this before, but when going through gunicorn, it seems that the only thing I can get from domain is what i have in my INI file used to configure Gunicorn - localhost.
I'm wondering if there is a way to get the actual, full domain name where the request originated? It can't be anything hard coded since the subdomains could be different for each request. Does anyone have any ideas how to make this happen?
UPDATE
I made the change requested by Fuero, changing the value I had for
proxy_set_header Host $http_host;
to
proxy_set_header Host $host;
Unfortunately, that didn't do it. I'm still seeing 127.0.0.1:6500 in the environment as the remote address, host, etc. The only thing that shows me the actual client request domain is the referrer. I'm including my config file below hoping something stills stand out.
user www-data;
worker_processes 4;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
# 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;
##
# Logging Settings
##
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
##
# Gzip Settings
##
# sendfile on;
gzip on;
gzip_http_version 1.1;
gzip_vary on;
gzip_comp_level 6;
gzip_proxied any;
gzip_types text/plain text/html text/css
application/json application/x-javascript
text/xml application/xml application/xml+rss
text/javascript application/javascript text/x-js;
gzip_buffers 16 8k;
gzip_disable "MSIE [1-6]\.(?!.*SV1)";
##
# Virtual Host Configs
##
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
upstream myapp-site {
server 127.0.0.1:6500;
}
server {
access_log /var/www/tmsenv/logs/access.log;
location / {
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;
client_max_body_size 10m;
client_body_buffer_size 128k;
proxy_connect_timeout 60s;
proxy_send_timeout 90s;
proxy_read_timeout 90s;
proxy_buffering off;
proxy_temp_file_write_size 64k;
proxy_pass http://myapp-site;
proxy_redirect off;
}
}
}
Since you're using WSGI, you can find the hostname in environ['HTTP_HOST'] See PEP 333 for more details and other information you can retrieve.
Add this to your config:
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
This preserves the server name that was entered in the browser in the Host: header and adds the client's IP address in X-Forwarded-For:.
Access them via environ['HTTP_HOST'] and environ['HTTP_X_FORWARDED_FOR']. WSGI might be smart enough to respect X-Forwarded-For: though when setting REMOTE_IP.
I finally got this working and the fix was something stupid, as typical. While digging for answers I noticed my config didn't have the root or static directory location for which I'm using nginx in the first place. I asked the person who set this up and they pointed out that it was in another config file, which is consumed via the include.
include /etc/nginx/sites-enabled/*;
I went to that file and added the suggested headers and it works.
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
I need some help from some linux gurus. I am working on a webapp that includes a comet server. The comet server runs on localhost:8080 and exposes the url localhost:8080/long_polling for clients to connect to. My webapp runs on localhost:80.
I've used nginx to proxy requests from nginx to the comet server (localhost:80/long_polling proxied to localhost:8080/long_polling), however, I have two gripes with this solution:
nginx gives me a 504 Gateway time-out after a minute, even though I changed EVERY single time out setting to 600 seconds
I don't really want nginx to have to proxy to the comet server anyway - the nginx proxy is not built for long lasting connections (up to half an hour possibly). I would rather allow the clients to directly connect to the comet server, and let the comet server deal with it.
So my question is: is there any linux trick that allows me to expose localhost:8080/long_polling to localhost:80/long_polling without using the nginx proxy? There must be something. That's why I think this question can probably be best answered by a linux guru.
The reason I need /long_polling to be exposed on port 80 is so I can use AJAX to connect to it (ajax same-origin-policy).
This is my nginx proxy.conf for reference:
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;
client_max_body_size 10m;
client_body_buffer_size 128k;
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
proxy_buffer_size 4k;
proxy_buffers 4 32k;
proxy_busy_buffers_size 64k;
proxy_temp_file_write_size 64k;
send_timeout 600;
proxy_buffering off;
Here's my nginx.conf and my proxy.conf. Note however that the proxy.conf is way overkill - I was just setting all these settings while trying to debug my program.
/etc/nginx/nginx.conf
worker_processes 1;
user www-data;
error_log /var/log/nginx/error.log debug;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/proxy.conf;
include /etc/nginx/mime.types;
default_type application/octet-stream;
access_log /var/log/nginx/access.log;
sendfile on;
tcp_nopush on;
keepalive_timeout 600;
tcp_nodelay on;
gzip on;
gzip_comp_level 2;
gzip_proxied any;
gzip_types text/plain text/html text/css application/x-javascript text/xml application/xml application/xml+rss text/javascript;
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
/etc/nginx/proxy.conf
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;
client_max_body_size 10m;
client_body_buffer_size 128k;
proxy_connect_timeout 6000;
proxy_send_timeout 6000;
proxy_read_timeout 6000;
proxy_buffer_size 4k;
proxy_buffers 4 32k;
proxy_busy_buffers_size 64k;
proxy_temp_file_write_size 64k;
send_timeout 6000;
proxy_buffering off;
proxy_next_upstream error;
I actually managed to get this working now. Thank you all. The reason nginx was 504 timing out was a silly one: I hadn't included proxy.conf in my nginx.conf like so:
include /etc/nginx/proxy.conf;
So, I'm keeping nginx as a frontend proxy to the COMET server.
i don't think, that is possible ...
localhost:8080/long_polling is a URI ... more exactly, it should be http://localhost:8080/long_polling ... in HTTP the URI would be resolved as requesting /long_polling, to port 80 to the server with at the domain 'localhost' ... that is, opening a tcp-connection to 127.0.0.1:80, and sending
GET /long_polling HTTP/1.1
Host: localhost:8080
plus some additional HTTP headers ... i haven't heard yet, that ports can be bound accross processes ...
actually, if i understand well, nginx was designed to be a scalable proxy ... also, they claim they need 2.5 MB for 10000 HTTP idling connections ... so that really shouldn't be a problem ...
what comet server are you using? could you maybe let the comet server proxy a webserver? normal http requests should be handled quickly ...
greetz
back2dos
Try
proxy_next_upstream error;
The default is
proxy_next_upstream error timeout;
The timeout cannot be more than 75 seconds.
http://wiki.nginx.org/NginxHttpProxyModule#proxy_next_upstream
http://wiki.nginx.org/NginxHttpProxyModule#proxy_connect_timeout
There is now a Comet plugin for Nginx. It will probably solve your issues quite nicely.
http://www.igvita.com/2009/10/21/nginx-comet-low-latency-server-push/
without doing some serious TCP/IP mungling, you can't expose two applications on the same TCP port on the same IP address. once nginx has started to service the connection, it can't pass it to other application, it can only proxy it.
so, either user another port, another IP number (could be on the same physical machine), or live with proxy.
edit: i guess nginx is timing out because it doesn't see any activity for a long time. maybe adding a null message every few minutes could keep the connection from failing.
You might want to try listen(80) on the node.js server instead of 8080 (i presume you are using that as an async server?) and potentially miss out Ngnix altogether. I use connect middleware and express to server static files and deal with caching that would normally be handled by Ngnix. If you want to have multiple instances of node running (which I would advise), you might want to look into node.js itself as a proxy / load balancer to other node instances rather than Nginx as your gateway. I ran into a problem with this though when I was serving too many static image files at once but after I put the images on S3 it stabilized. Nginx MAY be overkill for what you are doing. Try it and see. Best of luck.