Nginx not appending path when using proxy_path - http

I am trying to use Nginx as a reverse proxy for a few backends. It's probably not relevant, but the backends are all hosted within a single Docker network, and the network is being created using docker-compose.
The backends all have the same endpoints that need to be exposed, so to reduce duplicate configuration lines I am trying to use map.
map $http_host $backend {
host-a.example.com host-a;
host-b.example.com host-b;
}
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name _;
ssl_certificate /etc/ssl/cert.pem;
ssl_certificate_key /etc/ssl/key.pem;
location / {
# there is no DNS resolution if we set proxy_pass targets dynamically,
# use the docker internal DNS server
resolver 127.0.0.11;
proxy_pass "http://$backend:8080/";
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-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
The resolver line has been added as Nginx will otherwise give the error: no resolver defined to resolve *hostname*
However with this configuration I am finding that requests are being sent to the wrong path, for example if I send a request to https://host-a.example.com/books or https://host-a.example.com/books/123/front-cover.jpg the page that is returned is always the root of the site (i.e. https://host-a.example.com/), it seems that for some reason proxy_pass is not including the path with the request to the backend server.
Is there a way that I can make this work with map or do I need to create a separate server block for each of the backends that I want to proxy requests to.

From the proxy_pass documentation:
When variables are used in proxy_pass
. . .
In this case, if URI is specified in the directive, it is passed to the server as is, replacing the original request URI.
You should either remove the trailing / (the documentation calls this the optional URI) :
proxy_pass http://$backend:8080;
Or provide the full URL of the request (conveniently available as a built-in variable) :
proxy_pass http://$backend:8080$request_uri;

Related

nginx reverse proxy for application

I use nginx for reverse proxy with domain name. I've some application publish on IIS and i want to proxy different location name for each application.
For example;
Domain name on nginx :
example.com.tr
application end points for app:
1.1.1.1:10
1.1.1.2:10
upstream for app in nginx.conf:
upstream app_1 {
least_conn;
server 1.1.1.1:10;
server 1.1.1.2:10;
}
server {
listen 443 ssl;
server_name example.com.tr;
proxy_set_header X-Forwarded-Port 443;
ssl_certificate /etc/cert.crt;
ssl_certificate_key /etc/cert.key;
location /app_1/ {
proxy_pass http://app_1/;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-REAL-SCHEME $scheme;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
access_log /etc/nginx/log/access.log;
error_log /etc/nginx/log/error.log;
}
}
When I try to access example.com.tr/app_1/ , I can access application but not all data.
I inspected this site and so many requests of application were failed.
All requests sended to example.com.tr/uri instead of example.com.tr/app_1/uri. How can I fix this ?
thanks,
You need a transparent path proxy setup. Means NGINX should use the requested URI without removing the matched location from it.
proxy_pass http://app_1;
Remove the tailing slash to tell NGINX not to do so. Using an upstream definition is great but make sure you apply keepalive.

Nginx proxy with Docker containers

This is my docker infra.
nginx webserver serving on port 80 (exposed to host)
media and blog serving on port 80 (not exposed to host) running under individual container
All running on the same VM.
Name Command State Ports
----------------------------------------------------------------------------------
media docker-php-entrypoint apac ... Up 80/tcp
mysql_db_blog docker-entrypoint.sh mysqld Up 3306/tcp
mysql_db_media docker-entrypoint.sh mysqld Up 3306/tcp
webserver nginx -g daemon off; Up 0.0.0.0:80->80/tcp
blog docker-entrypoint.sh apach ... Up 80/tcp
My nginx config:
server {
listen 80 default_server;
server_name 192.168.0.7;
server_tokens off;
location /story/ {
proxy_pass http://blog/;
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-Host $server_name;
}
location /videos/ {
proxy_pass http://media/;
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-Host $server_name;
}
}
My issue:
When I try to access any of the below URLs
1) http://webserver/story
2) http://webserver/videos
after the first request, the last part 'story' or 'videos' gets deleted form the url.
What am I missing from nginx conf to make sure 'story' or 'videos' doesn't gets removed from the url?
It's to do with how proxy_pass is written in your stanza.
From proxy_pass:
A request URI is passed to the server as follows:
If the proxy_pass directive is specified with a URI, then when a request is passed to the server, the part of a normalized request URI matching the location is replaced by a URI specified in the directive:
location /name/ {
proxy_pass http://127.0.0.1/remote/;
}
If proxy_pass is specified without a URI, the request URI is passed to the server in the same form as sent by a client when the original request is processed, or the full normalized request URI is passed when processing the changed URI:
location /some/path/ {
proxy_pass http://127.0.0.1;
}
So change your proxy_pass:
location /videos/ {
proxy_pass http://media;
Note - lack of trailing slash, means it preserves the original URI.
You might also want to look at rewrite as that does something related.

Jenkins With Nginx Reverse Proxy And Resolver

I am running Jenkins inside Docker behind an Nginx reverse proxy. Now I got a problem with the resolver.
When I activate the resolver with:
set $backend "http://jenkins:8080/";
proxy_pass $backend;
I will get the following error for all javascript files:
Refused to execute script from 'http://localhost/static/....js' because its MIME type ('text/html') is not executable, and strict MIME type checking is enabled.
When I simply proxy pass without resolver:
proxy_pass http://jenkins:8080/;
It works but without resolver. The resolver is mandatory otherwise the setup won't work when the host jenkins changes it's Ip (Docker DNS Server).
My configuration:
resolver 127.0.0.11 ipv6=off valid=30s;
client_max_body_size 100m;
keepalive_timeout 65;
types_hash_max_size 2048;
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
location / {
set $backend "http://jenkins:8080/";
proxy_pass $backend;
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;
}
}
Based on the error message you receive, it sounds like you're getting HTML pages in place of JavaScript.
Using a proxy_pass paradigm with a variable, you're telling nginx that it shouldn't mess with the value any further, e.g., regardless of the location and the URI, all requests to your backend will always be just as the variable says (e.g., with the same URI in your case).
The best option would probably be to use $uri, $is_args and $args, as per NGINX proxy_pass remove path prefix & resolve DNS:
- set $backend "http://jenkins:8080/";
- proxy_pass $backend;
+ proxy_pass http://jenkins:8080$uri$is_args$args;
Another option, which potentially could be less secure, is to use $uri_request, which has a slightly different meaning than the plain $uri in certain limited cases, as per Nginx pass_proxy subdirectory without url decoding:
proxy_pass http://jenkins:8080$request_uri;

proxy pass configuration in nginx

I am a front end developer and tried my hands in nginx configuration last time which is working fine. The below is the configuration:
server {
listen 80;
server_name localhost;
access_log /var/log/nginx/localhost.access.log;
location / {
#By default route to node.js running on localhost:9000 port
proxy_pass http://localhost:9000;
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;
}
#currently only one server but will have to redirect to n hosts based on a parameter
location /hosts.json {
proxy_pass http://app-host.net:3000;
}
#currently only one server but will have to redirect to n hosts based on a parameter
location /hosts/ {
proxy_pass http://app-host.net:3000;
}
}
Now, I need to redirect to 4 different servers based on a parameter. ie if the city is Bangalore, I need to redirect to bangalore.corp.net:3000 and if the city is NewYork, then I need to redirect to newyork.corp.net:3000 and so on.
Here is somewhat I am expecting:
location /app1/hosts/ {
proxy_pass http://app1-host.net:3000;
}
#But the proxy pass should point to http://app1-host.net:3000/hosts and not http://app1-host.net:3000/app1/hosts
How can we handle such proxy pass in the nginx configuration file. Please let me know.
You have a URL of the form /app1/hosts/foo which should map to http://app1-host.net:3000/hosts/foo. The can be achieved by appending a URI in the proxy_pass directive, which will act like an alias.
location /app1/hosts/ {
proxy_pass http://app1-host.net:3000/hosts/;
}
See this document for details.

simple nginx reverse proxy seems to strip some headers

I am a beginner at nginx. I have a simple webserver on 8080 that I want to pass all traffic to in this rather small environment. My proxy seems to work except that a custom header is not there when it gets to my upstream server. The server block is below. What would I need to add to this to keep my custom header? In this case the custom header was set in angularjs but I don't think that has anything to do with it as it works fine going directly to 8080 on the server. ($httpProvider.defaults.headers.common['GH_client'] = client_id();)
server {
server_name localhost;
location / {
proxy_pass http://localhost:8080;
proxy_redirect off;
proxy_pass_header X-CSRF-TOKEN;
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-NginX-Proxy true;
}}
Thanks for any help.
Your header contains underscore (_). By default, nginx treats headers with an underscore as invalid and drops them.
You should enable underscores_in_headers directive.
Otherwise, consider changing the header name to one without underscores. GH-client will be perfectly valid and proxied to your backend server.

Resources