simple nginx reverse proxy seems to strip some headers - nginx

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.

Related

Nginx not appending path when using proxy_path

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;

Nginx location to FLASK route

I have 2 docker containers running. 1 is FLASK and 2 is Nginx.
This is the configuration of the Nginx location
location /search/?(.*) {
proxy_pass http://backend:8080/search/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
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_pass_request_headers on;
}
The FLASK is running on port 8080 and the route #app.route('/search', methods=['GET']) and running with app.config['SERVER_NAME'] = 'mydomain.com'
When I try to send a GET request to "mydomain.com" (going through the NGINX) from external I get
Cannot GET /search
When I remove app.config['SERVER_NAME'] = 'mydomain.com' from the FLASK I can send GET to EXTERNAL_IP:8080 (Directly to the backend not though the NGINX).
But when I leave the app.config['SERVER_NAME'] = 'mydomain.com' and try to GET mydomain.com:8080 I get 404 Not Found
What am I missing here?
I want all requests to go though the NGINX and I want to use mydomain.com
I think your main mistake is that you trying to match the whole request URI including the query part while location directive (as well as the rewrite one) works with the normalized URI which doesn't include the query part at all (check the location directive documentation to find out what URI normalization is). Looks like you are also trying to use a regex while regex location should be declared using a ~ (or ~*) modifier. Nevertheless you don't need any regex locations at all for your particular case. To proxy a single API endpoint preservig the Host HTTP header value, you can try this one:
location = /search {
proxy_pass http://backend:8080;
proxy_set_header Host $http_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;
}
You don't need to specify an URI with the proxy_pass directive since you are not changing your request URI. If your API endpoint is /search/ rather than /search, change the location accordingly. If it is an URI path prefix rather than a single API endpoint, use a prefix location /search/ { ... } instead of exact match location.

How to configure Nginx so the proxy_pass also aply to in-site requests?

So let me introduce you to my context: I have a several containers deployed and I'd like to use nginx as reverse proxy so I can target my other containers (which are API's) through something like this: https://example.mysite.com/services/whatever_the_api.
I managed to get to a point where the proxy_pass works like a charm but now I have a problem: when my API admin page loads, it tries to find the assets like this : https://example.mysite.com/assets/... ditching the /services/api/ part.
Here is my /etc/nginx/config.d/sample.conf file :
server {
listen myhost:80;
server_name myhost;
location ^~ /services/apiName/ {
proxy_pass http://myapp:1338/;
proxy_set_header X-Forwarded-Host $host:$server_port;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
If anyone knows what is the configuration material I'm missing, I'll be grateful to hear about it.

Replace Resources with Nginx Reverse Proxy

English is not my native language, so I use a translator. My questions may be a little difficult to understand, I hope you can understand.
I want to access B.com, but B.com is blocked and I can't access it directly. So I set up a reverse proxy server.
A.com points to my reverse proxy server, while B.com points to the source server.
My reverse proxy server is configured like this.
server
{
listen 80;
listen 443 ssl http2;
server_name A.com;
location /
{
proxy_pass https://B.com;
proxy_set_header Host B.com;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header REMOTE-HOST $remote_addr;
}
*other configurations*
}
However, some of the resources on the page are from www.B.com and are also blocked. So I added the following configuration.
sub_filter "B.com" "A.com";
sub_filter_types *;
sub_filter_once off;
proxy_set_header Accept-Encoding "";
This successfully replaced most of the content, but some of it did not. What should I do? Please help me ;(

Change Host header in nginx reverse proxy

I am running nginx as reverse proxy for the site example.com to loadbalance a ruby application running in backend server. I have the following proxy_set_header field in nginx which will pass host headers to backend ruby. This is required by ruby app to identify the subdomain names.
location / {
proxy_pass http://rubyapp.com;
proxy_set_header Host $http_host;
}
Now I want to create an alias beta.example.com, but the host header passed to backend should still be www.example.com otherwise the ruby application will reject the requests. So I want something similar to below inside location directive.
if ($http_host = "beta.example.com") {
proxy_pass http://rubyapp.com;
proxy_set_header Host www.example.com;
}
What is the best way to do this?
You cannot use proxy_pass in if block, so I suggest to do something like this before setting proxy header:
set $my_host $http_host;
if ($http_host = "beta.example.com") {
set $my_host "www.example.com";
}
And now you can just use proxy_pass and proxy_set_header without if block:
location / {
proxy_pass http://rubyapp.com;
proxy_set_header Host $my_host;
}
map is better than set + if.
map $http_host $served_host {
default $http_host;
beta.example.com www.example.com;
}
server {
[...]
location / {
proxy_pass http://rubyapp.com;
proxy_set_header Host $served_host;
}
}
I was trying to solve the same situation, but with uwsgi_pass.
After some research, I figured out that, in this scenario, it's required to:
uwsgi_param HTTP_HOST $my_host;
Hope it helps someone else.
Just a small tip. Sometimes you may need to use X-Forwarded-Host instead of Host header. That was my case where Host header worked but only for standard HTTP port 80. If the app was exposed on non-standard port, then this port was lost when the app generated redirects. So finally what worked for me was:
proxy_set_header X-Forwarded-Host $http_host;

Resources