You are right, you are using location
and proxy_pass
a wrong way. When you use the
location /vault {
proxy_pass http://vault:8200;
}
construction, you are passing your URI to the upstream as-is, while most likely you want to strip the /vault
prefix from it. To do it, you should use this one:
location /vault/ {
proxy_pass http://vault:8200/;
}
You can read more about the difference of the first and the second one here. However this still can prevent the assets from loading correctly.
This question – how to proxy some webapp under some URI prefix – is being asked again and again on stackoverflow. The only right way to do it is to made your proxied app request its assets via relative URLs only (consider assets/script.js
instead of /assets/script.js
) or using the right prefix (/vault/assets/script.js
). Some well-written apps are able to detect if they are used under such an URI prefix and use it when an asset link is being generated, some apps allows to specify it via some settings, but some are not suited for the such use at all. The reason why the webapp won’t work without fulfilling these requirements is quite obvious – any URL not started with /vault
won’t match your location /vault/ { ... }
block and would be served via main location
block instead. So the best way to do it is to fix your webapp, however several workarounds can be used if you really cannot.
-
Some web frameworks already builds their webapps with relative URLs, but uses a
<base href="/">
in the head section ofindex.html
. For example, React or Angular use this approach. If you have such a line within your webapp rootindex.html
, just change it to<base href="/vault/">
. -
Using conditional routing based on HTTP
Referer
header value. This approach works quite well for a single page applications for loading assets, but if a webapp contains several pages this approach won’t work, it’s logic for the right upstream detection would break after the first jump from one page to another. Here is an example:map $http_referer $prefix { ~https?://[^/]+/vault/ vault; # other webapps prefixes could be defined here # ... default base; } server { # listen port, server name and other global definitions here # ... location / { try_files "" @$prefix; } location /vault/ { # proxy request to the vault upstream, remove "/vault" part from the URI proxy_pass http://vault:8200/; } location @vault { # proxy request to the vault upstream, do not change the URI proxy_pass http://vault:8200; } location @base { # default "root" location proxy_pass http://consul:8500; } }
Update @ 2022.02.19
Here is one more possible approach using conditional rewrite:
server { # listen port, server name and other global definitions here # ... if ($http_referer ~ https?://[^/]+/vault/) # rewrite request URI only if it isn't already started with '/vault' prefix rewrite ^((?!/vault).*) /vault$1; } # locations here # ... }
-
Rewriting the links inside the response body using
sub_filter
directive fromngx_http_sub_module
. This is the ugliest one, but still can be used as the last available option. This approach has an obvious perfomance impact. Rewrite patterns should be determined from your upstream response body. Usually that type of configuration looked likelocation /vault/ { proxy_pass http://vault:8200/; sub_filter_types text/css application/javascript; sub_filter_once off; sub_filter 'href="/' 'href="/vault/'; sub_filter "href="https://stackoverflow.com/" "href="http://stackoverflow.com/vault/"; sub_filter 'src="/' 'src="/vault/'; sub_filter "src="/" "src="/vault/"; sub_filter 'url("/' 'url("/vault/'; sub_filter "url('/" "url('/vault/"; sub_filter "url(/" "url(/vault/"; }
Update @ 2022.02.19
Related thread at the ServerFault: How to handle relative urls correctly with a nginx reverse proxy.
Possible caveats using sub_filter
on the JavaScript code: Nginx as reverse proxy to two nodejs app on the same domain.