NGINX proxy_pass rewrite asset uri - nginx

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;
}

Related

Nginx proxy-pass to other domain extracting domain part from URI

I'm trying to use Nginx as a proxy server which should redirect all requests to different domains based on the URI parameters.
Say we have two servers org1-domain.com and org2-domain.com. The incoming request is mynginxproxy.com/api/org2/users. In this case, Nginx should proxy the request to the org2-domain.com.
Here's my Nginx config file:
user nginx;
worker_processes 1;
events {
worker_connections 10240;
}
http {
server {
listen 8080 ssl;
server_name nginx-proxy;
location ^/\w*/(.*)/.*$ {
proxy_pass https://$1-domain.com:8080;
}
}
}
So I'm using a regex in the location directive in order to get org parameter from the URI and use it in the proxy_pass directive. But I'm always getting this error:
host not found in upstream "-domain.com" in /etc/nginx/nginx.conf:15
I also tried other options for regex in the location directive:
location ~ ^/\w*/\w*/(.*)/(.*)$ {
location ~* /(.*)$ {
location ~* /(.*)$ {
But in all those cases I'm always getting the same host not found error.
I also tried to use rewrite rules instead of proxy_pass but in this case, Nginx just returns me a 302 redirect response which is not suitable for my case.
BTW proxy_pass without regex works fine if I'm redirecting directly to the org2-domain.com:
location / {
proxy_pass https://org2-domain.com:8080;
}
But I need somehow to extract org from the URI and construct the DNS name for proxy_pass directive.

Nginx - how to proxy_pass particular url to a different host while keeping context?

I have a fairly simple nginx conf for a front-end and backend application:
server {
listen 8080;
server_name nginx_server;
port_in_redirect off;
location / {
root /dir/html;
index index.html index.htm;
}
location /api/ {
proxy_pass http://my-api:8080/;
}
}
My main host is https and this works fine. When I hit https://myapp.com/api/a/b/c, my backend receives /a/b/c, which is what I want.
Now, I want to send the requests to a particular /api context to a different server:
location /api/a/b {
rewrite /api/(.*) /$1 break;
proxy_pass http://another-api:8080;
}
So now, if I hit https://myapp.com/api/a/b?param=1 I want to hit http://another-api:8080/a/b?param=1 and also https://myapp.com/api/a/b/c/d should hit http://another-api:8080/a/b/c/d
This is working when I test using Postman, but for some reason, in Chrome, when my frontend app tries to hit https://myapp.com/api/a/b/c/d I get a console error: (blocked:mixed-content)
How can I fix this?

Nginx Fails with my variables in location

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;
}

Nginx: how to add /something to a uri and still keep it working

I have a nginx instance running. My config is something like the following.
server {
listen 80;
listen 443;
location / {
...
proxy_pass http://127.0.0.1:8080;
...
proxy_redirect http://127.0.0.1:8080 example.com;
}
}
I have some software running in 8080 and I want that the user enters example.com/somepath and be able to be redirected to the root 127.0.0.1:8080 through my domain. The software should receive all urls without /somepath but the browser should still show /somepath in the name.
I am quite new so sorry for the basic question I could not find any relevant info on how to do this exactly: I tried rewrite rules and setting location /mysoftware { tests with no luck.
The client browser uses /somepath/... to access /...in the application. This means that nginx must rewrite the URI before passing it upstream.
The proxy_pass directive has a basic rewrite capability. See this document for details. For example:
location /somepath/ {
proxy_pass http://127.0.0.1:8080/;
...
}
Alternatively, you might use a rewrite ... break statement. See this document for details. For example:
location /somepath {
rewrite ^/somepath/?(.*)$ /$1 break;
proxy_pass http://127.0.0.1:8080;
...
}
The difficult part is preventing your application from breaking out of /somepath. The proxy_redirect directive can handle the 3xx responses from your application. But the location of resource files (.css and .js) and the target for hyperlinks, can cause problems for applications that are not aware that they need to stay inside a subdirectory.

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