proxy_set_header not working as expected - nginx

I'm trying to have nginx proxy various applications on subpaths of the same domain.
My problem is that the links generated by the application use / as their root instead of their subdir.
My configuration is :
location /wiki/ {
proxy_pass http://localhost:4567/;
proxy_set_header SCRIPT_NAME /wiki;
}
I believe proxy_set_header SCRIPT_NAME /wiki; should set the header SCRIPT_NAME, which is use by the application to generate links, but instead HTTP_SCRIPT_NAME is set, which is ignore by the application.
How can I set SCRIPT_NAME so that my links are generated correctly ?

As per CGI specification, http headers are availabe with the HTTP_ prefix:
Meta-variables with names beginning with "HTTP_" contain values read
from the client request header fields, if the protocol used is HTTP.
The HTTP header field name is converted to upper case, has all
occurrences of "-" replaced with "_" and has "HTTP_" prepended to
give the meta-variable name.
That is, header Some-Header will be seen as HTTP_SOME_HEADER in your application. That is, everything works is expected - you added http header, and got it available with HTTP_ prefix.
The SCRIPT_NAME variable is special and isn't set by any header, but instead it's constructed from URI by the code which runs your application. To change it, you have to actually change URI seen by your backend, i.e. you need
proxy_pass http://localhost:4567/wiki/;
Or just no /wiki/ in proxy_pass, as long as it's in location /wiki/ anyway, i.e.
location /wiki/ {
proxy http://localhost:4567;
}
The bad thing here is that you probably did URI change from /wiki/ to / for some reason, i.e. your backend application expects /. There are several possible solutions to this problem:
Actually move the application to /wiki/. Usually this is simple to do.
Change your app to accept it's base url used for generating links etc. by some out-of-bands method. Many application already support this via some configuration option.
Try to replace what your application returns by nginx itself. There are several nginx directives to do it, in particular proxy_redirect, proxy_cookie_path, and the sub filter. It's most fragile method though, and not recommended unless you know what your application returns and what exactly have to be replaced.

Related

Nginx Reverse Proxy with digital ocean spaces returning bad request because of unicode characters

I have following location block in my server configuration:
location ~^/media/(.+)/(.+)$ {
error_log /home/user/Server/nginx/logs/error.log;
access_log /home/user/Server/nginx/logs/access.log;
proxy_pass https://bucketname.sgp1.digitaloceanspaces.com/$1/$2;
}
The problem is if I use english character filenames in second variable of regular expression then it works fine.
But if we pass any unicode Hindi character as second variable of regular exp then it returns bad request.
In my access log it looks like this:
How to fix this issue? so that it can easily pass unicode characters in filename to Digital Ocean Spaces server.
The reason you've got an HTTP 400 Bad Request error is that unicode characters in URI should been percent-encoded according to RFC 3986. However for some reasons (especially to match internationalized filenames on the local filesystem) nginx works with the normalized URI, and one of the normalization steps is decoding such URIs (check location directive documentation to find out more details about the URI normalization). There are no built-in instruments to re-encode those matched URI parts again (although some third-party modules could do the job). However an original non-normalized request URI is available to you via the $request_uri nginx internal variable, so you can take these URI parts from it using the following map block (should be placed outside the server block at the http configuration level):
map $request_uri $mediafile {
~^/media/([^/?]+/[^/?]+)(?:$|\?) $1;
}
Then inside your server block you can use the following location instead (no need to use capture groups here anymore):
location ~ ^/media/[^/]+/[^/]+$ {
...
proxy_pass https://bucketname.sgp1.digitaloceanspaces.com/$mediafile;
}

Nginx forwarding to different app's different path using `#` symbol

Quick question. We have two apps. Ports 3001 and 3002. Our domain is www.domain.com.
What we want to have it once person enters www.domain.com/pathname we want them to be redirected into another app's specific path.
How to do it?
We already came up to this in my nginx
location /pathname/ {
proxy_pass http://127.0.0.1:3002/;
}
It nearly works. However, our app under 3002 works on path /#/pathname.
We can access it by typing www.domain.com/pathname/#/pathname. We want to access same link by typing www.domain.com/pathname.
How to shorten it? What do I miss?
(upd) Just redirect /pathname to /pathname/#/pathname
According to your comment, you want just redirect from /pathname to /pathname/#/pathname
Try these combined directives:
rewrite to append # and fragment identifier
and proxy_pass to reverse proxy to the app.
E.g.:
location /short_path_name/ {
rewrite ^ /pathname/#/$uri permanent;
break;
}
location /pathname/ {
proxy_pass http://127.0.0.1:3002/;
}
And use www.domain.com/short_path_name/ link for your app.
Unfortunately, nginx can't see the fragment identifier
Unfortunately, you can't. Because server never get the fragment identifier from browser.
The fragment identifier functions differently to the rest of the URI: its processing is exclusively client-sided with no participation from the web server
Naming a bit amusing, but it has a long history. See TBL (1997): Fragment Identifiers on URIs:
The URI reference is a thing you build by taking a URI for an information object, adding a "#" sign and then a Fragement identifier. (The last term is historical, so try not to thinl of it necessarily identifying a fragment).
Workarounds
There are workarounds, e.g. encode hashtag symbol into %23 but I'm not sure is it your way.
Handle request arguments with nginx
Note: rewriting url, nginx can preserve request arguments if you add ? at the end of rewrite directive.
See Nginx rewrite manual:
If a replacement string includes the new request arguments, the previous request arguments are appended after them. If this is undesired, putting a question mark at the end of a replacement string avoids having them appended, for example:
rewrite ^/users/(.*)$ /show?user=$1? last;

How to create a dynamic root in Nginx based on a route value?

What I'm trying to accomplish is to be able to pass in a route such as this:
mysite.com/abc123/file.mp3
In Nginx I want it to read the abc123 and then call a piece of code (don't care what language: php, python, golang, fortran...) and then return the actual key that is needed to load the file.
In my config I have this:
server{
#lots of basic stuff here
location / {
mp4;
mp4_buffer_size 1m;
mp4_max_buffer_size 5m;
root /my_path/;
}
}
This works when I pass in my abc123/file.mp3. It will find the file and play it if that file exists in /my_path/abc123/file.mp3.
What I want is to translate (from a database) abc123 to myKey123 which would live at /my_path/myKey123/file.mp3
So, first, is this even possible?
If so, I'm not sure how to approach this. I know this question could have multiple solutions, but any direction will be appreciated.
The PHP script can issue a redirection using the X-Accel-Redirect header. This is treated by nginx as an internal redirect, so the rewritten URI is not exposed to the user. The documentation is a bit thin on details, hopefully you can find an example using PHP.
If you can ring-fence the rewritten URIs with a unique prefix, e.g. /download/myKey123/file.mp3, you can protect the files from direct access by using the internal directive. See this document for details. In which case, you will not need to obfuscate myKey123.
location /download/ {
internal;
...
}

How do I tell TYPO3 what it's URL is when using a reverse proxy?

I have:
A url www.my-website.com
A machine running TYPO3 with some hostname like typo3-website.my.internal.domain.com
A machine running Nginx which uses proxy_pass to send requests from www.my-website.com to typo3-website.my.internal.domain.com
A DNS A record for www.my-website.com pointing to the reverse-proxy machine.
When a backend user is working in the Page module, and they do right-click 'Show' on a page, it tries to open the page at the hostname of the machine TYPO3 is running on.
I want it to open the page under the actual website URL instead.
What setting do I need to change to make this work?
Things I have already tried which did not help:
Setting the trusted hosts pattern
'reverseProxyIP' => '*'
'reverseProxyHeaderMultiValue' => 'last'
I needed to create a domain record. That partially fixed it.
This still leaves problems where images use the wrong URL in the backend.
This means, that, for example, the little dotted lines in the page tree module do not display.
To fix that, I used a clue from this forum thread. I needed to set the HTTP_HOST variable to my domain and send it to PHP fpm:
location ~ \.php$ {
try_files $uri = 404;
include ${pkgs.nginx}/conf/fastcgi.conf;
fastcgi_pass unix:/var/run/phpfpm/default.sock;
# Assorted fastcgi_blah
fastcgi_param HTTP_HOST "www.my-domain.com";
}
If you're trying to get TYPO to see the request as a request for a specific domain, you may want to use the following on your proxy block:
proxy_set_header Host $proxy_host;

NGinx : How to test if a cookie is set or not without using 'if'?

I am using the following configuration for NGinx currently to test my app :
location / {
# see if the 'id' cookie is set, if yes, pass to that server.
if ($cookie_id){
proxy_pass http://${cookie_id}/$request_uri;
break;
}
# if the cookie isn't set, then send him to somewhere else
proxy_pass http://localhost:99/index.php/setUserCookie;
}
But they say "IFisEvil". Can anyone show me a way how to do the same job without using "if"?
And also, is my usage of "if" is buggy?
There are two reasons why 'if is evil' as far as nginx is concerned. One is that many howtos found on the internet will directly translate htaccess rewrite rules into a series of ifs, when separate servers or locations would be a better choice. Secondly, nginx's if statement doesn't behave the way most people expect it to. It acts more like a nested location, and some settings don't inherit as you would expect. Its behavior is explained here.
That said, checking things like cookies must be done with ifs. Just be sure you read and understand how ifs work (especially regarding directive inheritance) and you should be ok.
You may want to rethink blindly proxying to whatever host is set in the cookie. Perhaps combine the cookie with a map to limit the backends.
EDIT: If you use names instead of ip addresses in the id cookie, you'll also need a resolver defined so nginx can look up the address of the backend. Also, your default proxy_pass will append the request onto the end of the setUserCookie. If you want to proxy to exactly that url, you replace that default proxy_pass with:
rewrite ^ /index.php/setUserCookie break;
proxy_pass http://localhost:99;

Resources