nginx: Index file outside of the document root - nginx

I'm trying to serve a directory of user-provided files with nginx, but with an index file that is outside of the document root. The reason I don't want to locate the index file inside the directory is because the directory is meant for users to drop their stuff in there.
Nginx's index directive seems to only work for files inside the document root; the documentation says that the index can be an "absolute path", but my experiments tell that this is only relative to the document root.
So, I tried to serve another index location with location =/index.html { alias /path/to/index.html }, and this manages to work when /index.html is directly requested, but it doesn't work if / is requested (403 with log "directory index of "/srv/docroot/" is forbidden"). It starts to work, though, if I create an empty file to /srv/docroot/index.html; then /path/to/index.html is served at /. So it seems that Nginx
Checks if /srv/docroot/index.html file exists in the filesystem at the /'s location.
If it exists, it does an internal redirect, and serves the /path/to/index.html at /index.html location.
What is the correct way to serve an index file outside of the document root?

index and try_files directives, provided with absolute paths, seem only to be able to point to the files inside the current document root. (With relative ../ paths you can point outside of the document root, but that's not ideal if you want to point to an absolute path in the filesystem.)
It seems that only the alias directive can point outside the document root. I was able to get my setup to work with:
...
location / {
try_files $uri /index.html;
}
location =/index.html {
alias /path/to/index.html;
}
...
This doesn't strictly answer to the question in the sense, that in this case, index.html isn't shown only when / is requested, but always when a matching file isn't found. I'm happy with this solution, but it might make sense in some cases to separate the 404 error.

Related

How to not need a .dotted file path in nginx configuration

I'm trying to serve my AppLink for google association services. The following works:
location /.well-known/assetlinks.json {
root /var/www/static/google-association-service/;
types { } default_type "content-type: application/json";
}
Provided I have the correct file placed at
/var/www/static/google-association-service/.well-known/assetlinks.json
The URI is what it it is, Google gets to decide that, but I'd rather not have it resolve to a hidden file at the bottom of a directory where the next guy wonders why the directory is even there because he forgot to ls with a '-a'. Is there a way to make it so I can map this URI to something like:
/var/www/static/google-association-service/assetlinks.json # omit the hidden sub directory
?
(I've tried understanding the difference between root and alias, but I'm not seeing how alias would help me here)
alias basically gives you the possibility to serve a file with another name (e.g. serve a file named foo.json at location /.well-known/assetlinks.json). Even if this is not required in your case I would favor this config, as it is easily understandable:
location = /.well-known/assetlinks.json {
alias /var/www/static/google-association-service/assetlinks.json;
}
Note that the = is required to not match locations like /.well-known/assetlinks.json1111.
You can use try_files to find the specific file you want.
For example:
location = /.well-known/assetlinks.json {
root /var/www/static/google-association-service;
try_files /assetlinks.json =404;
}
Or:
location = /.well-known/assetlinks.json {
root /var/www/static;
try_files /google-association-service/assetlinks.json =404;
}
The concatenation of the root value and the try_files parameter form the path to the file.

Nginx Match Image Filename Based On Part of URL?

For local development, I'm trying to serve static content out of a public directory. My images live in app/public/images, and my root is app/public. My images are looked up at /d/my_app_name/images/<image>.png. I'm matching the /d/my_app_name/images location, but trying to figure out how to then extract the file name and have nginx look up the appropriate filename from the images directory? My current settings:
root /app/public;
index index.php;
location /d/my_app_name/images/ {
root /images/; # <--- I guess this rule is making nginx look up a file that matches the entire relative path, rather than just the filename
}
UPDATE
I was able to get it to match and look up the filename only from what appears to be the right directory, but I'm still getting a not found error. I'm assuming that the relative path shown in the error is relative to the root, in which case that file/directory should exist.
root /app/public;
index index.php;
location /d/my_app_name/images/ {
alias /images/;
}
When I go to localhost:8380/d/my_app_name/images/my_image.png it is looking up the correct png file in the app/public/images directory, but it's still saying it doesn't exist.
[error] 8#0: *1 open() "/images/my_image.png" failed (2: No such file or directory), client: 192.168.176.1, server: devbox, request: "GET /d/my_app_name/images/my_image.png HTTP/1.1", host: "localhost:8380"
Images are kept in /app/public/images directory.
I think you have inverted location and root paths.
I can suggest you to change your location block as follow:
location /images/ {
root /d/my_app_name/images/; # override global root to your specific path where your images are hosted
}
More configurations examples can be found on the official documentation.
You can use a simple alias which tells Nginx to search for all the files in another path. Root would only work if your location was just images and is actually used in the documentation: https://nginx.org/en/docs/http/ngx_http_core_module.html#alias
root /app/public; #Use full path, ex: /home/username/repo/app/public/
index index.php;
location /d/my_app_name/images/ {
alias /app/public/images/; # Use full path, ex: /home/username/repo/app/public/
}

nginx check if *.pdf file exists in folder

I think I have a problem understanding how I can check if a file exists in a specific folder with nginx.
for example, I use this url:
www.domain.tld/folder/filename.pdf
now, I think I have to check it like that:
location /folder/.(pdf)$ {
}
is that correct?
and then, if it is the right way, how can I redirect if the file doesn't exists?
I'm new and from apache os it is a bit hard for men to switch in my mind
The location directive matches the requested URI, but does not decide if the file exists. The contents of the location block determines the action if the file exists or not, and the simplest way to accomplish that is using try_files.
The try_files directive will test if the file exists and internally redirect to another URI if it does not.
For example:
location ~ ^/folder/.*\.pdf$ {
try_files $uri /another/uri;
}

Nginx try and serve from two different directories

I am trying to serve a wordpress application and a static site from two separate folders located inside the web-root folder.
/usr/local/nginx/html <- Root
/usr/local/nginx/html/application/wordpress/wordpress-files*
/usr/local/nginx/html/files/static-site-files*
Goal: I want to prioritise the files in /application/ and look in there first before trying /files/. If the file is not found in /application/, look and serve from /files/.
I've been reading up on try_files and it seems to be the way to go, but I have been unsuccessful.
I have tried the following location block...
location / {
index index.php index.html index.htm;
root /usr/local/nginx/html;
try_files /application$uri /application$uri/ /files$uri /files$uri/ =404;
}
That mostly works but there are a few issues.
If i visit http://serverip.com/wordpress/, the wordpress site shows, but if i visit http://serverip.com/wordpress, it redirects to /application/wordpress/.
Another issue is that if I visit http://serverip.com, I get a forbidden error due to nginx trying to list a directory when it is not allowed.
I understand why this is occurring, ( /application$uri/ looks for an index file that is not there, so it tries to display the directory, rather than moving on to /files$uri) but this is not the behaviour I want. I want nginx to fall back to looking in /files/.
Is try_files suitable for my use-case?
Thanks

conditional rewrite or try_files with NGINX?

I'm having trouble setting up a conditional rewrite, and I've been trying to use the if directive (despite all sources indicating it's "evil") with the -f switch to check for the presence of a file, but it's not working. I believe the issue/case is best explained by example, so here goes:
Directory structure
workspace/
myapp/
webroot/
index.php
assets/
baz.js
hello/
foo.js
modules/
hello/
assets/
foo.js
bar.js
Expected results
/ => /workspace/myapp/webroot/index.php
/assets/hello/foo.js => /workspace/myapp/webroot/assets/hello/foo.js
/assets/hello/bar.js => /workspace/myapp/modules/hello/assets/foo.js
/assets/baz.js => /workspace/myapp/webroot/assets/baz.js
In summary:
foo.js is only present in the modules/hello/assets folder and gets delivered from there.
bar.js is present both in webroot/assets/hello and modules/hello/assets and gets delivered from webroot.
(it hides/overrides the file in modules)
baz.js is only present in webroot/assets and gets delivered from there.
The part that doesn't work right now, is this:
location /assets/ {
if (-f $uri) {
break;
}
root /workspace/myapp/modules;
rewrite ^/assets/([^/]+)/(.*)$ /$1/assets/$2 break;
}
Namely the if directive, doesn't seem to have any affect - the bar.js file gets delivered from modules rather than webroot.
Should I be using if or not?
Is there any way I can solve this problem with try_files instead? I can't seem to grasp how this would work together with rewrite which I can't seem to get around.
Please do not suggest reorganizing the assets using a deploy script or something - it's not an option, for various other reasons.
I have used this pattern with Apache before, and NGINX seems more capable in most respects, so I'm sure this must be possible?
One requirement that isn't absolute, is I don't have to be able to override modules/hello/assets/foo.js with webroot/assets/hello/foo.js - serving scripts from webroot/assets/* is however a requirement.
The answer is divided into two parts: the first part explains why your configuration does not work and the second one provides examples of how to solve your problem. If you are only interested in the solution, go straight to the second part.
The problem
First of all, note that the positon of the root directive in a location block is not important. It does not matter if you put it at the very top or at the bottom of a location, it will affect the whole location anyway. Also, keep in mind that break in the end of the rewrite line tells Nginx to stay within the current location even if the URI has been successfully rewrited.
Having said that, let's take a look at your configuration and see how every request from the Expected results is processed and why nothing works as expected.
Let's presume that there is no other suitable location with a higher priority in your configuration. Since every request from Expected results starts with /assets, all of them will be handled according to the rules presented in your location. So:
/assets/hello/foo.js
The root is set to /workspace/myapp/modules. The if directive will be evaluated to false, because /assets/hello/foo.js does not exist and so break will not be executed. Finally, the last rewrite will change the requested URI from /assets/hello/foo.js to /hello/assets/foo.js and the following break will tell Nginx to stay within the current location. As a consequence /workspace/myapp/modules/hello/assets/foo.js will be served.
/assets/hello/bar.js
This request is processed exactly the same way as the previous one, so /workspace/myapp/modules/hello/assets/bar.js will be served.
/assets/baz.js
Yet again the root is set to /workspace/myapp/modules and the if is evaluated to false. But this time the final rewrite will not change the URI, because the request does not match the regular expression. As a consequence Nginx will try to serve /workspace/myapp/modules/assets/baz.js and since there is no such file exists, will return 404.
As you can see your configuration cannot possibly work as you want it to for several reasons:
if is always evaluated to false, because you try to check URIs and not files;
the request stays within the location because you tell it to stay there with break in the rewrite line;
root is always set to /workspace/myapp/modules in this location so no file can be served from anywhere else.
The solutions
The easiest solution would be to use try_files:
root /workspace/myapp/webroot;
location /assets/ {
try_files $uri #modules;
}
location #modules {
root /workspace/myapp/modules;
rewrite ^/assets/([^/]+)/(.*)$ /$1/assets/$2 break;
}
This configuration tells Nginx to look for a file in the webroot folder first and if nothing is found then go to the modules folder in another location. This approach is considered most preferable.
On the other hand, using if would allow you to solve the problem within one location:
location /assets/ {
root /workspace/myapp; # The parent folder
if (-f $document_root/webroot/$uri) {
rewrite ^(.*)$ /webroot/$1 break;
}
rewrite ^/assets/([^/]+)/(.*)$ /modules/$1/assets/$2 break;
}
However, this approach is considered outdated is not recommended for use.

Resources