nginx - adding custom header to upstream - http

I am trying to use nginx to set some additional custom request headers to upstream. But it is not working (no headers are set). Any help would be appreciated.
location /someUrl {
proxy_pass_request_headers on;
proxy_set_header Test ABCDE;
proxy_pass http://example.com;
}

Try reversing the order the sets like :
location /someUrl {
proxy_pass http://example.com;
proxy_set_header Test ABCDE;
proxy_pass_request_headers on;
}
I have seen this fix before but can not explain it has the documentation doesn't say anything.

Related

How does proxy_redirect correctly replace the Location URL?

I am using the following Nginx reverse proxy configuration.
server {
listen 80;
listen [::]:80;
server_name www.example.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $Host;
proxy_set_header x-forwarded-for $remote_addr;
proxy_set_header X-Real-IP $remote_addr;
add_header Cache-Control no-store;
add_header Pragma no-cache;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}
The vast majority of the time, it works well. However, problems occur when visiting URLs that have 301 redirects set.
For example, when visiting http://www.example.com/aaa/, it should redirect to http://www.example.com/bbb/
but it redirects incorrectly to http://127.0.0.1:3000/bbb/
After investigating this issue, I found that I should use the proxy_redirect parameter instead.
I found a description of proxy_redirect on the Nginx website, but I'm sorry that I didn't fully understand it.
As I understand it, the following usage can be used to solve my problem. The reason for not using a regular expression to get the rest of the URL is that it is automatically appended to the replacement string (am I right?) .
proxy_redirect http://$proxy_host/ /;
I also have two questions.
1、For non-80 ports, the $proxy_host variable should also output the port, something like 127.0.0.1:3000. But why does the official Nginx example use http://$proxy_host:8000/, is this a bug? Or is there something I'm not understanding?
2、Suppose that accessing http://www.example.com is incorrectly redirected to http://127.0.0.1:3000. In this case, does the above substitution syntax not work, because its substitution condition is at least http://127.0.0.1:3000/ (the / character is missing). How should I solve this problem? Should I use a regular expression to get the full URL for replacement? If so, is the following syntax correct?
proxy_redirect ~*http://127.0.0.1:3000(.*)$ http://www.example.com$1;
Thank you for your patience in reading, and thanks in advance!

Sharing location configuration in nginx

I could not decide the best name for the question.
Essentially what I want to achieve is to set a custom allowed body size for a specific location on the webserver.
On the other hand, I was able to achieve the necessary result already with duplicate code, so I am really looking for a way how to make the code reusable and to better understand the observed behavior.
The server reverse-proxies all API requests to the backend service.
In global nginx config /etc/nginx/nginx.conf I set the rule for max allowed body size like so client_max_body_size 50k;.
Then, in individual server config /etc/nginx/conf.d/example.com I have the following config (simplified):
server {
listen 80;
listen [::]:80;
server_name api.example.com www.api.example.com
location ~* /file/upload {
client_max_body_size 100M;
# crashes without this line
proxy_pass http://localhost:90;
#proxy_pass http://localhost:90/file/upload; # also works
}
location / {
# does not work
#location ~* /file/upload {
# client_max_body_size 100M;
#}
proxy_pass http://localhost:90;
}
}
I am trying to override the max body size for file upload endpoint. See that there is 1 proxy_pass for location /file/upload and another proxy_pass for location / pointing to the same internal service.
Question 1. If I remove the proxy_pass from the location /file/upload then error is returned by the server. (no status code in chrome debugger). Why is this happening? Shouldn't request be propagated further to location /?
Question 2. Why is it not possible to define the sublocation with body size override inside the / location as in commented section in example above? If I set it like this, then 413 error code is returned, which hints that the client_max_body_size rule is ignored..
Question 3. Finally, is it possible to tell nginx, after the request hits the /file/upload location - to apply all the rules from the / section? I guess one solution to this problem would be to move the common configuration into separate file and then import it in both sections.. I was thinking if there is any solution that does not require creating new files?
Here is the reusable config I am talking about basically:
location / {
#.s. kill cache. use in dev
sendfile off;
# kill cache
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;
# don't cache it
proxy_no_cache 1;
# even if cached, don't try to use it
proxy_cache_bypass 1;
proxy_pass http://localhost:90;
client_max_body_size 100M;
proxy_http_version 1.1;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $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;
proxy_set_header Host $http_host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_pass_request_headers on;
}
This is not the final version. If I had to copy this piece of code to 2 sections this would not be very friendly approach. So, it would be nice to hear some nginx lifehacks on how accomplish what I try to accomplish in a most friendly way and get some explanations for observed behavior.
Answer 1
If I remove the proxy_pass from the location /file/upload then error is returned by the server. (no status code in chrome debugger). Why is this happening?
Every location have a so-called content handler. If you don't specify content handler explicitly via proxy_pass (fastcgi_pass, uwsgi_pass, etc.) directive, nginx will try to serve the request locally.
Shouldn't request be propagated further to location /?
Of course not. What makes you think it should?
Answer 2
Why is it not possible to define the sublocation with body size override inside the / location as in commented section in example above? If I set it like this, then 413 error code is returned, which hints that the client_max_body_size rule is ignored..
I'd rather expect you'll get the same error as in the first case since your nested location does not have an explicitly specified content handler via the proxy_pass directive. However the following config is worth to try:
location / {
# all the common configuration
location /file/upload {
client_max_body_size 100M;
proxy_pass http://localhost:90;
}
proxy_pass http://localhost:90;
}
Answer 3
Finally, is it possible to tell nginx, after the request hits the /file/upload location - to apply all the rules from the / section?
No, unless you use a separate file via include directive in both locations. However you can try to move all the upstream related setup directives one level up to the server context:
server {
...
# all the common configuration
location / {
proxy_pass http://localhost:90;
}
location /file/upload {
client_max_body_size 100M;
proxy_pass http://localhost:90;
}
}
Note that some directives (e.g. add_header, proxy_set_header) are inherited from the previous configuration level if and only if there are no those directives defined on the current level.
Very often dynamic settings for different locations can be achieved using the map block in a following way:
map $uri $max_body_size {
~^/file/upload 100M;
default 50k;
}
server {
location / {
...
client_max_body_size $max_body_size;
...
}
}
Unfortunally not every nginx directive accepts variable as its argument. Usually when nginx documentation doesn't explicitly states that some directive can accept variables, it means it cannot, and the client_max_body_size is exactly that kind of directive, so the above configuration won't work.

NGINX - using sub_filter to change a path in a proxy pass location block

So I have an NGNIX server that receives traffic from the same location (Akamai) and based on the path of the incoming URL I send the traffic to different applications.
I have added a new origin to Akamai and this now means that some incoming requests now have a new path. The problem is that my application needs the path to be a certain value.
As I am sharing the Akamai slot with other origins I can't send the request with the same path to two different origins as the slot gets confused as to which origin server to direct the traffic at.
So what I would like to do is change the path before passing it to the application.
I am not sure on the best way to do this and need some assistance.
Should I use rewrite, redirect or sub_filter?
I have actually tried all three but I am missing something in this very simple task.
location /incoming_path {
max_ranges 0;
proxy_set_header Accept-Encoding "";
proxy_pass https://\$upstream_application:9002;
proxy_ssl_server_name on;
proxy_ssl_certificate /etc/nginx/conf.d/server_cert.pem;
proxy_ssl_certificate_key /etc/nginx/conf.d/server_key.pem;
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$proxy_protocol_addr;
sub_filter_types *;
sub_filter "https://\$proxy_host/incoming_path" "https://\$host/new_path"
sub_filter_once on;
}
Really would appreciate any ideas/thoughts on how to achieve this, thanks in advance.
This looks to have worked:
location /incoming_path {
max_ranges 0;
rewrite /incoming_path/(.*) /new_path/$1 break;
proxy_pass https://\$upstream_application:9002;
proxy_ssl_server_name on;
proxy_ssl_certificate /etc/nginx/conf.d/server_cert.pem;
proxy_ssl_certificate_key /etc/nginx/conf.d/server_key.pem;
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$proxy_protocol_addr;
}

Nginx proxy_pass shows a blank page for wix.com sites

Basically, I have the same problem as
Proxying site via nginx results in blank page and https://serverfault.com/questions/850923/nginx-proxy-wix-site-shows-only-blank-page-in-browser-or-not-found, however there are no "real" solutions provided and I still have the problem of a blank page
This is my location block
location /compliance {
proxy_set_header Accept-Encoding "";
sub_filter 'wixdomain.wixsite.com' '$host';
sub_filter_once off;
proxy_pass http://wixdomain.wixsite.com/compliance;
}
However, I still see the blank page, I tried multiple other things, such as
sub_filter_types text/html text/javascript application/x-javascript or adding proxy_pass_request_headers on; or adding proxy_set_header Host $host but none of them worked.
Does anyone have an idea why this is happening? no css is loaded, every js is loaded without error (200). I'm not sure what I can do anymore to fix this issue. Could this be related that I'm on localhost:{PORT} and wix somehow disables it anyway for localhost? Should I try it out with a domain or do you see something else what is already wrong here?
In my current company we have a purchase flow (wrote in PHP) to buy a product and landing pages built in wix. We use a proxy to all our landing pages are in the same domain that our purchase flow. Our nginx proxy configuration is the following.
server {
listen 443;
location / {
proxy_ssl_server_name on;
gzip off;
proxy_set_header Accept-Encoding "";
add_header Host mi-portal.wix.com;
proxy_pass_request_headers on;
proxy_http_version 1.1;
proxy_pass https://mi-portal.wix.com/;
}
// Location to use a backend as PHP
}
Adding onto Victor's answer, since it wasn't sufficient for us, as we have included a form on our landingpage.
To serve the wix content at OURDOMAIN.com/info, we used the following configuration:
server {
listen 443;
location ~ ^/info(\/.*)?$ {
resolver 8.8.8.8;
proxy_set_header Accept-Encoding "";
proxy_pass https://XYZ.wixsite.com/info$1;
sub_filter "XYZ.wixsite.com" "OURDOMAIN.com";
sub_filter_once off;
}
location ~ ^/_api(\/.*)?$ {
resolver 8.8.8.8;
proxy_pass https://XYZ.wixsite.com/_api$1;
}
# ...
}
We had to add a sub_filter and setup a proxy_pass back to the wix API.

Nginx pass_proxy subdirectory without url decoding

I need to write an nginx location directive to proxy requests to subdirectory to another server preserving urlencoding and removing subdirectory prefix.
Here's an artificial example — request like this:
http://1.2.3.4/api/save/http%3A%2F%2Fexample.com
should pass as
http://abcd.com/save/http%3A%2F%2Fexample.com
I tried several different ways. Here're couple of them:
From this SO question
location /api/ {
rewrite ^/api(/.*) $1 break;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_pass http://abcd.com;
}
But it decodes the string, so http://abcd.com gets /save/http://example.com
From another SO question
location /api/ {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_pass http://abcd.com;
}
But it keeps subdirectory, so http://abcd.com gets /api/save/http%3A%2F%2Fexample.com.
What's needed is somewhere in the middle. Thank you!
UPD: Here's a ticket in nginx bug tracker
But there is no easy way to fix this nginx behaviour. There are some bugs in nginx trac, you could add yours. trac.nginx.org/nginx/…. So, I think that the simplest way is to have subdomain. – Alexey Ten Feb 24 '15 at 14:49
https://trac.nginx.org/nginx/ticket/727
If you want nginx to do something custom, you can do so using ​proxy_pass with variables (and the $request_uri variable, which contains original unescaped request URI as sent by a client). In this case it will be your responsibility to do correct URI transformations. Note though that this can easily cause security issues and should be done with care.
Challenge accepted!
location /api/ {
rewrite ^ $request_uri;
rewrite ^/api/(.*) $1 break;
return 400;
proxy_pass http://127.0.0.1:82/$uri;
}
That's it, folks!
Here's for the full proof.
The config file for nginx/1.2.1:
server {
listen 81;
#first, the solution
location /api/ {
rewrite ^ $request_uri;
rewrite ^/api/(.*) $1 break;
return 400; #if the second rewrite won't match
proxy_pass http://127.0.0.1:82/$uri;
}
#next, a few control groups
location /dec/ {
proxy_pass http://127.0.0.1:82/;
}
location /mec/ {
rewrite ^/mec(/.*) $1 break;
proxy_pass http://127.0.0.1:82;
}
location /nod/ {
proxy_pass http://127.0.0.1:82;
}
}
server {
listen 82;
return 200 $request_uri\n;
}
Here are the results of running the queries for each location:
% echo localhost:81/{api,dec,mec,nod}/save/http%3A%2F%2Fexample.com | xargs -n1 curl
/save/http%3A%2F%2Fexample.com
/save/http:/example.com
/save/http:/example.com
/nod/save/http%3A%2F%2Fexample.com
%
Note that having that extra return 400; is quite important — otherwise, you risk having a security issue (file access through //api etc), as Maxim has briefly mentioned in your trac ticket.
P.S. If you think using the rewrite engine as a finite-state automaton is super cool, you might also want check out my http://mdoc.su/ project, or fork it github.
What you have to do is fairly easy as long as we are talking prefix matching with ^~ or no modifier
location /api/ {
# if you don't want to pass /api/ add a trailing slash to the proxy_pass
proxy_pass http://localhost:8080/;
...
}
And everything will be passed along without decoding, you don't have to pass $uri
Also while you use proxy pass you should also set these headers
# pass headers and body along
proxy_pass_request_headers on;
proxy_pass_request_body on;
# set some headers to make sure the reverse proxy is passing along everything necessary
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;

Resources