Nginx Fails with my variables in location - nginx

So I am trying to set up an nginx default.conf and I'm having trouble using variables. I want to capture the subdomain as the $subdomain variable and use it a few times in the default.conf.
Here is my config:
server {
listen 80;
server_name ~^(?<subdomain>.+)\.example\.com$;
# To allow special characters in headers
ignore_invalid_headers off;
# Allow any size file to be uploaded.
# Set to a value such as 1000m; to restrict file size to a specific value
client_max_body_size 0;
# To disable buffering
proxy_buffering off;
location / {
rewrite ^/$ /$subdomain/index.html break;
proxy_set_header Host $http_host;
proxy_pass http://minio-server:9000/$subdomain/;
#health_check uri=/minio/health/ready;
}
}
Unfortunately the presence of the $subdomain variable in the location block fails nginx entirely every time. If I were to replace $subdomain in the location block with tester as a static value then everything works.
How to correctly use the $subdomain variable here???
This question is a somewhat of a followup on this issue: k8s-ingress-minio-and-a-static-site. In that issue I was trying to use Ingress to reverse proxy to a minio bucket, but to no avail. Now I'm just trying to go through Nginx directly but my vars ain't workin.
Updates
So it seems the proxy_pass will not resolve the host correctly if there is a variable in the URL.
Tried two things:
set the resolver like so: resolver default.cluster.local. I tried a bunch of combos for the fqdn of the kube-dns but to no avail and kept getting minio-server can't be found.
Simply don't use variables like Richard Smith mentions below. Instead rewrite everything then proxy pass. However I don't understand how this would work and I get very useless errors like so: 10.244.1.1 - - [07/Feb/2019:18:13:53 +0000] "GET / HTTP/1.1" 405 291 "-" "kube-probe/1.10" "-"

According to the manual page:
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.
So you need to construct the complete URI for the upstream server.
For example:
location = / {
rewrite ^ /index.html last;
}
location / {
proxy_set_header Host $http_host;
proxy_pass http://minio-server:9000/$subdomain$request_uri;
}
It may be better to use rewrite...break and use proxy_pass without a URI.
For example:
location / {
rewrite ^/$ /$subdomain/index.html break;
rewrite ^ /$subdomain$uri break;
proxy_set_header Host $http_host;
proxy_pass http://minio-server:9000;
}

Related

Is there a preferred nginx config to run wsgidav behind a reverse proxy?

so far I simply use
location /drive/ { # wsgidav
proxy_pass http://127.0.0.1:8080/;
}
and it seems to do the trick. I can put files to the server, get them, browse through directories etc. all from Windows explorer.
However, I can not rename a file on the server. When I attempt it, I get 502 Bad Gateway
14:57:44.803 - INFO : 127.0.0.1 - (anonymous) - [2022-10-14 12:57:44] "MOVE /user/Downloads/Text.txt" dest="https://myserver.com/drive/user/Downloads/Text1.txt", length=0, depth=0, overwrite=F, elap=0.001sec -> 502 Bad Gateway
Am I missing anything in the configuration?
Thx
Sry for the noise, found one myself.
Will just leave this here in case others find it useful.
There is a closed issue regarding the problem that files can't be renamed behind a reverse proxy. One solution suggested to "Configure the reverse proxy to rewrite the Destination header's protocol from 'https:' to 'http:'".
I followed this suggestion and added a mapping rule outside of the config's server section
map $http_destination $driveDestination { # otherwise MOVE results in a 502 Bad Gateway
default $http_destination;
"~*^https://myserver.com/drive/(.+)" /$1;
}
and the following location for the webdav drive
## Begin - wsgidav
location /drive/ { # trailing slash means it will be added
proxy_pass http://127.0.0.1:8080/; # - trailing slash means location will be dropped
# https://github.com/mar10/wsgidav/issues/183
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_buffering off;
client_max_body_size 0;
proxy_request_buffering off;
proxy_set_header Destination $driveDestination; # result of map command above
}
## End - wsgidav
And, alas, it works.

Replicate proxy_pass location behavior with variables

So usually when creating a nginx location it would look something like this:
location /foo/ {
proxy_pass http://example.com/;
}
With this setup, requests to /foo/bar are forwarded to http://example.com/bar which is the intended behavior.
However when trying to prevent caching of the domain name example.com or when trying to prevent nginx from crashing if the upstream host is unavailable at startup the only solution seems to be to not use the target directly in the proxy_pass directive, but to instead create a variable containing the target like this:
location /foo/ {
set $targetUri http://example.com/;
proxy_pass $targetUri;
}
But this totally changes the setup. As soon as proxy_pass contains a variable, it no longer appends anything to the target uri, as the nginx docs describe:
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.
So requests to /foo/bar are simply forwarded to http://example.com/.
When bringing $request_uri into the mix, more than what we want is appended:
location /foo/ {
set $targetUri http://example.com$request_uri;
proxy_pass $targetUri;
}
Requests to /foo/bar are now forwarded to http://example.com/foo/bar.
The only workaround I have found is to resort to regex patterns for the location:
location ~ ^/foo/(.*)$ {
set $targetUri http://example.com/$1$is_args$args;
proxy_pass $targetUri;
}
Is there any way to replicate the behavior of proxy_pass when using variables without having to regex-match the location? The reason I want to avoid regex is because the location path is based on a user input from which the location block is generated.
Remove the trailing / from your $targetUri variable so that proxy_pass does not have the "optional URI" part in its value. Then use rewrite...break to duplicate the original behaviour.
For example:
location /foo/ {
set $targetUri http://example.com;
rewrite ^/foo(.*)$ $1 break;
proxy_pass $targetUri;
}

NGINX proxy_pass rewrite asset uri

I'm trying to do a basic NGINX reverse proxy by subdomian, to localhost/folder and am stumped getting it to rewrite my assets+links.
My http://localhost:8080/myapp/ works like a charm, but via NGINX+subdomain it fails on the subfolder assets.
I believe I'm stumped on the 'rewrite' clause for NGINX.
How can I rewrite the HTML going to the client browser to drop the /myapp/ context?
server {
listen 443 ssl;
server_name app1.domain.com;
location / {
rewrite ^/myapp/(.*) /$1 break; # this line seems to do nothing
proxy_pass http://localhost:8080/myapp/;
}
}
I'm expecting my resultant HTML (via https://app1.domain.com) to be rewritten without the subfolder /myapp/, so when assets are requested they can be found instead of a 404 against https://app1.domain.com/myapp/assets/. It should just be https://app1.domain.com/assets/ (which if I manually go there they work)
--thanks.
Feeding from Ivan's response and finalizing my solution as:
server {
listen 443 ssl;
server_name app1.domain.com;
location / {
sub_filter '/myapp/' '/'; # rewrites HTML strings to remove context
sub_filter_once off; # ensures it loops through the whole HTML (required)
proxy_pass http://localhost:8080/myapp/;
}
}
As nginx proxy_pass documentation states:
In some cases, the part of a request URI to be replaced cannot be determined:
...
When the URI is changed inside a proxied location using the rewrite directive, and this same configuration will be used to process a request (break):
location /name/ {
rewrite /name/([^/]+) /users?name=$1 break;
proxy_pass http://127.0.0.1;
}
In this case, the URI specified in the directive is ignored and the full changed request URI is passed to the server.
So with this configuration block after you rewrite /myapp/assets/some_asset URI to /assets/some_asset and use a break flag, nginx ignores /myapp/ suffix on a proxy_pass directive and passes /assets/some_asset request to your backend. However strange it is, what you need is to use this rewrite rule instead:
rewrite ^(/myapp/.*)$ $1 break;
Another (may be even better) solution is to use two location blocks:
location / {
proxy_pass http://localhost:8080/myapp/;
}
location /myapp/ {
proxy_pass http://localhost:8080;
}

Nginx Reverse Proxy Help Streaming MP4 File [duplicate]

We have a couple of backends sitting behind our nginx front ends.
Is it possible to intercept 301 / 302 redirects sent by these backends and have nginx handle them?
We were thinging something alone the lines of:
error_page 302 = #target;
But I doubt 301/302 redirects can be handled the same as 404's etc etc... I mean, error_page probably doesnt apply to 200, etc error codes?
So to summarize:
Our backends send back 301/302s once in a while. We would like to have nginx intercept these, and rewrite them to another location block, where we could do any number of other things with them.
Possible?
Thanks!
You could use proxy_redirect directive:
http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_redirect
Nginx will still return 301/302 to the client but proxy_redirect will modify Location header and the client should make a new request to the URL given in the Location header.
Something like this should make the subsequent request back to nginx:
proxy_redirect http://upstream:port/ http://$http_host/;
I succeeded in solving a more generic case when a redirect location can be any external URL.
server {
...
location / {
proxy_pass http://backend;
# You may need to uncomment the following line if your redirects are relative, e.g. /foo/bar
#proxy_redirect / /;
proxy_intercept_errors on;
error_page 301 302 307 = #handle_redirects;
}
location #handle_redirects {
set $saved_redirect_location '$upstream_http_location';
proxy_pass $saved_redirect_location;
}
}
Alternative approach, which is closer to what you describe, is covered in ServerFault answer to this question: https://serverfault.com/questions/641070/nginx-302-redirect-resolve-internally
If you need to follow multiple redirects, modify Vlad's solution as follows:
1) Add
recursive_error_pages on;
to location /.
2) Add
proxy_intercept_errors on;
error_page 301 302 307 = #handle_redirect;
to the location #handle_redirects section.
More on proxy_redirect, for relative locations
Case
location /api/ {
proxy_pass http://${API_HOST}:${API_PORT}/;
}
the backend redirects to a relative location, which misses the /api/ prefix
the browser follows the redirection and hits a wall of incomprehension
Solution
location /api/ {
proxy_pass http://${API_HOST}:${API_PORT}/;
proxy_redirect ~^/(.*) http://$http_host/api/$1;
}

Nginx - how to return a proxy generated file?

Description:
I want to implement an http server (using nginx) that serves static files.
If the requested file doesn't exist, nginx shall send a request to a service (REST API) that will create the file and return its path.
After that, I want nginx to return the static file that was created.
Question:
What is the best way to return the file after its creation?
So far I managed to do this by changing the REST API in order to return the created file path with the 302 status code and with a location header as a redirect, but I am not sure if this is a good thing to do. Is it?
Is there any nginx-side solution for this? Do I have to create a custom module?
Conf file:
http {
server {
listen 80;
location /files {
try_files $uri #rest;
}
location #rest {
rewrite ^(.*)$ /api/ break;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_pass http://localhost:8080;
}
}
}
Edit: Actually, on balance, this should be even simpler:
location #rest {
...
proxy_intercept_errors on;
error_page 404 = $uri;
}
Configure the named location to intercept an "error" coming back (I chose 404), and then using the error_page directive will cause the given URI to be loaded again. Since the file now exists, the request should succeed.
Side note: I had thought try_files $uri #rest $uri would have worked, but an internal redirection only happens for the last argument.
The simplest option here is probably for your REST service to use X-Sendfile/X-Accel to return the relevant URI that Nginx should serve once the file is created. Your REST service could return the target URI using the header X-Accel-Redirect.
In your case, your API could actually just return the same URI it received as the X-Accel-Redirect header, and then Nginx would re-use the same location block and find the file for the subrequest occurring.
If this fails, however, using an internal Nginx location as per the examples at http://wiki.nginx.org/XSendfile and http://wiki.nginx.org/X-accel:
location /files-protected {
internal;
root /path/to/files;
}
and returning the relevant URI to that would also work.

Resources