nginx sends header from different location directive - nginx

I have the following nginx config, which basically has two locations which matches
a) routes including a dot (e.g. "/js/script.js") and
b) all other routes.
server {
listen 80;
server_name localhost;
root /app;
# files
# for all routes matching a dot, check for files and return 404 if not found
# e.g. /file.js returns a 404 if not found
location ~ \. {
add_header Cache-Control "public, max-age=2678400";
try_files $uri $uri/ =404;
}
# normal routes
# serve given url and default to index.html if not found
# e.g. /, /user and /foo/bar will return index.html
location / {
add_header Cache-Control "no-store";
try_files $uri $uri/ /index.html;
}
}
All routes matching a dot should be cached with the cache-control header and should return a 404 if not found.
Routes not matching a dot, e.g. "/users", should send the index.html by default.
However, this results in really strange behavior.
I have an index.html with the contents <h1>wazzup</h1>, and it gets correctly served when requesting a route without a dot, e.g. "/asdf":
$ curl -v localhost:8000/asdf
> Cache-Control: public, max-age=2678400
> Accept-Ranges: bytes
> ...
> <h1>wazzup</h1>
However, a Cache Control header is sent too, although this is defined in another location block. To summarize, the result is correct, but somehow the header gets mixed up. Why?
To try for yourself, you can run a docker image like so:
docker run -p 8000:80 -v /tmp/test:/app steebchen/nginx-spa
Create an index.html and script.js in the directory /tmp/test, and then access it:
curl -v localhost:8000/asdf // should return the contents of index.html
curl -v localhost:8000/script.js
The full Dockerfile is available on Github.

The answer is actually quite simple, I just figured it out by myself.
The second location is matching because the fallback is index.html and obviously this route contains a dot.
I simply edited the location to \.(?!html) which matches everything but html files:
location ~ \.(?!html) {
add_header Cache-Control "public, max-age=2678400";
try_files $uri $uri/ =404;
}

Related

try_files vs an empty location block in NGINX?

Wondering about the difference between doing:
location / {
try_files $uri $uri/ =404;
}
And doing:
location / {}
They both seem to serve files\folders only if they exist, or returning 404 error page if they don't.
There is no difference, try_files $uri $uri/ =404; is exactly the default nginx behavior:
Check if $document_root$uri is a file, and if it is, serve the request with the contents of this file;
Check if $document_root$uri is a directory, and if it is, serve the request with the first found index file from this directory. If no index files found, nginx will return directory list if you have autoindex on in your nginx config or HTTP 403 Forbidden error otherwise;
Return HTTP 404 Not found if $document_root$uri nor a file neither a directory.
Advantage of the try_files directive is that you can specify additional files/directories to check and select what to do if none of them are found (jump to another URI / jump to some named location / return any HTTP error code).
However try_files may give you some performance penalty depending of your settings, read this article by #DanilaVershinin for more details.

Serve Jekyll blog on Nginx

I am trying to serve a jekyll blog on nginx.
The files in the build directory should be reachable via the following routes:
- index.html -> /
- 1.0/
- index.html -> /1.0/
- foo/
a.html -> /1.0/foo/a/
b.html -> /1.0/foo/b/
c.html -> /1.0/foo/c/
- bar/
1.html -> /1.0/bar/1/
2.html -> /1.0/bar/2/
I tried to use the try_files directive in nginx but it always calls the fallback although the files are available. Here is the config:
location ~* ^(.+)/$ {
try_files $uri /$1/index.html /$1.html =404;
}
If I remove the 404 fallback it only works for the last value.
So my question is: What's the best way to configure nginx for serving static files like this?
If I remove the 404 fallback it only works for the last value.
This is because the last parameter of try_files directive should be an HTTP error code or an URI to try if the file not found. In your case nginx assumes it is an URI.
Try this:
location ~ ^(?<path>/.*/)(?<file>[^/]+)/$ {
try_files $uri $path$file.html $uri/ =404;
}
If you want to serve http://example.com/1.0/foo/a requests similar to http://example.com/1.0/foo/a/, change regex to ^(?<path>/.*/)(?<file>[^/]+)/?$.

nginx serve index file from subfolders

I would like to serve an index file from a root folder and one from a subfolder from root.
My nginx server conf looks as follows:
server {
listen 80;
server_name localhost;
root /data/;
index index.html index.htm index.txt;
location / {
}
location /a {
alias /data/a/;
}
}
and my directory structure looks as follows:
/data/:
a index.txt
/data/a:
index.txt
If I then do curl localhost, I get the contents of the file /data/index.txt.
But curl /localhost/a gives me a 301.
curl localhost/a/index.txt works.
Why can't I access my index.txt with curl /localhost/a ?
I tried using a root instead of alias in the location /a block and also tried to specify the index.txt for location /a, but no success.
I see similar posts, e.g.
Nginx location configuration (subfolders)
but couldn't yet find the answer.
The index directive works with URIs that end with a /:
So the URI / gives you the contents of /index.txt and /a/ gives you the contents of `/a/index.txt.
If you provide Nginx with a URI of a directory (but without a trailing /), the default behaviour is to redirect to the same URI, but with a trailing /.
This is just how the index directive works. See this document for details.
If you want something other than default behaviour you will have to do it manually using try_files. See this document for details.
For example, to return the contents of an index.txt file by providing the URI of the directory without a trailing /, use:
root /data;
location / {
try_files $uri $uri/index.txt =404;
}
Note that the location /a { ... } block is not necessary in either this example, or the example in your question.

Nginx try_files not working for default index.html

I am using $geoip_country_code module of nginx to redirect user based on IP. My config code is as given below-
server {
listen 80;
gzip on;
server_name example.com;
root html/example;
location / {
index index.html;
try_files /$geoip_country_code/index.html /index.html;
}
location ~* \.(gif|jpg|jpeg|png|js|css)$ { }
}
Idea is to redirect the user based on country for which I have localised otherwise the user goes to default index i.e generic for everyone else. It works perfectly when opened in browser from my country as I have localised for it. But when opened from a different country it shows internal server error. Its not going to default index page.
Can someone point me what am I doing wrong?
The last element of the try_files statement is the default action, which is either a URI or a response code. The /index.html starts a search for a new location to process the request, and ends up back at the start, so you have a redirection loop.
You can fix the problem by making /index.html a file term instead. For example:
try_files /$geoip_country_code/index.html /index.html =404;
The =404 is never actioned, because /index.html always exists.
Personally, I would use a generic solution:
try_files /$geoip_country_code$uri $uri /index.html;
See this document for more.

Nginx rewriting pushstate static resources wont load

I am trying to get nginx to work with a pushstate based uri handled by react-router.
Everything works fine until I try to F5 on a second level uri example.com/MyApp/users.
My static resources are in example.com/MyApp/resources.
The problem is that nginx is trying to load my resources in example.com/MyApp/users/resources whenever I try to access directly (or F5) the users's view.
Here is my nginx conf :
location ~ ^/MyApp/ {
try_files $uri /MyApp/index.html last;
}
I am new to nginx so I don't really know how everything works...
EDIT :
I changed my conf to this:
location / {
if (!-e $request_filename){
rewrite ^(.*)$ /MyApp/index.html break;
}
}
Now accessing to example.com/MyApp/users works but example.com/MyApp/users/ doesn't.
With client side app paths:
/
/foo
/foo/bar
/foo/bar/baz
/foo/bar/baz/123
/tacos
/tacos/123
Use nginx configuration:
server {
listen 80;
server_name example.com;
root /var/www/example.com;
gzip_static on;
location / {
try_files $uri $uri/ /index.html;
}
# Attempt to load static files, if not found route to #rootfiles
location ~ (.+)\.(html|json|txt|js|css|jpg|jpeg|gif|png|svg|ico|eot|otf|woff|woff2|ttf)$ {
try_files $uri #rootfiles;
}
# Check for app route "directories" in the request uri and strip "directories"
# from request, loading paths relative to root.
location #rootfiles {
rewrite ^/(?:foo/bar/baz|foo/bar|foo|tacos)/(.*) /$1 redirect;
}
}
This configuration will work within a pushState "directory" such as example.com/foo/bar/baz/213123 and resolve static files at relative paths like js/app.js to example.com/js/app.js instead of example.com/foo/bar/baz/js/app.js.
For cases with directory depth beyond the first level such as /foo/bar/baz, note the order of the directories declared in the #rootfiles directive: the longest possible paths need to go first, followed by the next shallower path /foo/bar and finally /foo.
See this related answer to a similar question regarding Backbone.
I think you will have to do something like this:
location ~ ^/MyApp/ {
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
try_files $uri $uri/ /index.html =404;
}
location ~ ^/MyApp/resources {
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
try_files $uri $uri/ /resources/index.html =404;
}

Resources