Location not working for files but only for path - nginx

I have a nginx.conf that looks like this:
server {
...
root /var/opt/data/web;
...
location ~* \.(?:eot|woff|woff2|ttf|js)$ {
expires 1M;
}
...
location /one {
root /var/opt/data/alternatives;
try_files $uri $uri/ =404;
}
location /two {
root /var/opt/data/alternatives;
try_files $uri $uri/ =404;
}
}
when I curl http://localhost/one/ I get the content of index.html stored in /other. But when I curl .../localhost/one/foo.js the file is not found and I get this in the error.log:
open() "/default/foo.js" failed (2: No such file or directory)
I tried other variants like location ~ (one|two), location /one/ or even location ~ /(one|two) but all of them didn't work.
The complete config consists of a lot more locations, but I guess the cause of my problem is the location where I set up .js resources to expire -1 because this prevents changing the root to what I need.
If this matters: I use nginx 1.15.2. In case you are wondering why I have this strange alternatives directory: the web directory is created by a CMS software while alternatives is git pulled.

nginx chooses a one location to process a request. Your location ~* \.(?:eot|woff|woff2|ttf|js)$ block processes any URI that ends with .js, and its root value is inherited from the outer block as /var/opt/data/web.
Where you have multiple roots, you need to ensure that those location blocks take precedence, by using the ^~ modifier. See this document for details.
For example:
server {
...
root /var/opt/data/web;
...
location ~* \.(?:eot|woff|woff2|ttf|js)$ {
expires 1M;
}
...
location ^~ /one {
root /var/opt/data/alternatives;
try_files $uri $uri/ =404;
location ~* \.(?:eot|woff|woff2|ttf|js)$ {
expires 1M;
}
}
...
}
If you need your expires rule to apply to the other roots, you will need to repeat the location within that scope, as shown above.
As an alternative, the expires directive can be used in conjunction with a map. See this document for details.
For example:
map $request_uri $expires {
default off;
~*\.(eot|woff|woff2|ttf|js)(\?|$) 1M;
}
server {
...
root /var/opt/data/web;
expires $expires;
...
location ^~ /one {
root /var/opt/data/alternatives;
try_files $uri $uri/ =404;
}
...
}

Related

Why nginx add_headers doesn't work properly?

I'm using Nginx as a web server.
This is my nginx.conf file:
server {
listen 80;
root /usr/share/nginx/html;
index index.html index.htm;
location ^~ /start/ {
add_header 'Cross-Origin-Embedder-Policy' 'require-corp';
add_header 'Cross-Origin-Opener-Policy' 'same-origin';
try_files $uri $uri/ /index.html;
}
location / {
try_files $uri $uri/ /index.html;
}
}
When I open this link on the browser, I don't see the header in the network tab in the response headers section:
https://example.com/start/629852d359d2a400034698a2
Actually add_header directive works properly. Most probably headers does not get added since there are no /usr/share/nginx/html/start/629852d359d2a400034698a2 file or directory on your server, so request gets rewritten to /index.html according to the last try_files directive parameter, which in turn being processed by your location / { ... } (since that new URI does not start with a /start/ prefix), and that location does not set any additional headers.
Generally, if those /start/-prefixed URIs could be either internal application routes or external assets links, this would be possible to solve using the map block to evaluate required headers values:
map $uri $add_policies {
~^/start/ 1;
# default value will be an empty string, unless specified explicitly
}
map $add_policies $embedder_policy {
1 require-corp;
}
map $add_policies $opener_policy {
1 same-origin;
}
server {
...
location / {
add_header Cross-Origin-Embedder-Policy $embedder_policy;
add_header Cross-Origin-Opener-Policy $origin_policy;
try_files $uri $uri/ /index.html;
}
}
This solution is based on the add_header behavior, which is not to add the specified header to the response at all if the provided value is an empty string.
However, if you know for sure the URIs where those headers should be added are an app routes rather than links to physically existing files, you have one more option:
server {
...
location ^~ /start/ {
set $embedder_policy require-corp;
set $origin_policy same-origin;
rewrite ^ /index.html last;
}
location / {
add_header Cross-Origin-Embedder-Policy $embedder_policy;
add_header Cross-Origin-Opener-Policy $origin_policy;
try_files $uri $uri/ /index.html;
}
}
This solution should be somewhat more performant since it does not require (some kind of expensive) PCRE library call to perform the regex matching operation.
Update
Reviewing my answers, I just figured out that the last configuration can be made even more simple using rewrite ... break instead of rewrite ... last:
server {
...
location ^~ /start/ {
add_header Cross-Origin-Embedder-Policy require-corp;
add_header Cross-Origin-Opener-Policy same-origin;
rewrite ^ /index.html break;
}
location / {
try_files $uri $uri/ /index.html;
}
}

Nginx separate apps in subdirectories

I am trying to move an app from apache server to nginx. The problem is that there are multiple apps in subdirectories and I can't find a proper way to configure the server.
What I need:
www.example.com serves from /srv/app
www.example.com/sub1 serves from /srv/app/sub1
www.example.com/sub2 serves from /srv/app/sub2
Each of the apps need the same config, so I extracted that in a snippet:
# snippets/app.conf
index index.php index.html index.htm index.nginx-debian.html;
location /system {
return 403;
}
# [a couple of other 403s excluded]
# Pass non-file URI to index.php
location / {
try_files $uri $uri/ /index.php?$query_string;
}
# Use PHP
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
}
# Hide .htaccess
location ~ /\.ht {
deny all;
}
And in the main server file:
# [non-www and http redirects]
server {
# [listen directives]
server_name www.example.com;
root /srv/app;
include snippets/app.conf;
location /sub1 {
root /srv/app/sub1;
include snippets/app.conf;
}
# [other sub-apps included in the same way]
# [ssl stuff]
}
However, this gives me an error:
nginx: [emerg] location "/system" is outside location "/sub1" in /etc/nginx/snippets/app.conf:5
It's obvious from the error that /system is interpreted as being "absolute" www.example.com/system instead of the nested www.example.com/sub1/system. Can I somehow specify that I want the nested locations to be considered relative? Or I just have to repeat the whole near-identical config for every sub-app changing the prefixes?
It turns out that most of the repeating is not necessary in nginx.
The directives to use fastcgi for .php and hide /.ht files were already regexes so they affect everything. It's enough to specify index once and the default there stuff was redundant if I only want to use index.php.
As all the apps are nested on the filesystem in the same way as on web, specifying root was not necessary either.
What surprised me was that location ^~ /(system|data)/ { ... } matches not only www.example.com/system/, but also www.example.com/sub1/system/. I thought that ^~ should match only if the location start matches the regex...
# [non-www and http redirects]
server {
# [listen directives]
server_name www.example.com;
root /srv/app;
index index.php;
location ^~ /(system|data)/ {
return 403;
}
# Use PHP
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
}
# Pass non-file URI to index.php for all locations
location /sub1/ {
try_files $uri $uri/ /sub1/index.php?$query_string;
}
location /sub2/ {
try_files $uri $uri/ /sub2/index.php?$query_string;
}
# [other sub-apps included in the same way]
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ /\.ht {
deny all;
}
# [ssl stuff]
}
I also tried to replace the separate locations with
location ^~ /(sub1|sub2)/ {
try_files $uri $uri/ /$1/index.php?$query_string;
}`
but didn't succeed with that - this location somehow didn't ever match and everything got passed to the /index.php in base instead.

Serve .min.css files first nginx

I need to configure nginx server.
I have folder with css files there are files some_file.min.css but in html website ask for some_file.css. I need to redirect this request to *min.css and if minified file doesnt exist return normal .css file
location ~* \.(css)$ {
root /usr/share/nginx/html;
try_files $1.min.$2 $uri =404;
expires 30d;
}
This piece of code does not work.
You need to split the URI into two parts and insert the .min. sequence in between. Your try_files statement is probably ok, but you have not captured the correct $1 and $2 variables.
You need to capture that part of the URI before the .css. For example:
location ~* ^(.*)\.css$ {
root /usr/share/nginx/html;
try_files $1.min.css $uri =404;
expires 30d;
}
Try this:
location ~* ^(.*)(?<!\.min)\.(js|css|html)$ {
try_files $1.min.$2 $uri =404;
}

Use nginx directive to look for a jpg in another location

I have a general nginx rule to serve jpgs from their URI.
So if the URI is "http://example.com/images/1.jpg" it will serve under the form root of the site/images/1.jpg
I want to try and serve the image from alternative path if not found on the original path. How do I write the second location?
Here is what I got:
location ~* ^.+\.(jpg|jpeg|gif|png|svg|js|css|mp3|ogg|mpe?g|avi|zip|gz|bz2?|rar|swf|ico)$ {
try_files $uri $uri/ #fallback;
access_log off;
expires max;
log_not_found off;
}
How do I write the fallback to look for the files in another location like /home/user/anotherfolder?
You can use a named capture in the regular expression location to save the filename for later. You can cascade named locations, to try different roots:
location ~* ^/images(?<filename>/.+\.jpg)$ {
try_files $uri #other;
}
location #other {
root /path/to/alternate;
try_files $filename #fallback;
}
If there is a suitable common parent directory, you can achieve the same thing in a single location block.
location ~* ^/images(?<filename>/.+\.jpg)$ {
root /path/to/ancestor;
try_files /dir/$uri /other/dir/$filename #fallback;
}

Multiple Location For Static Location in Nginx Configuration

I have a two locations where my app will serve static files, one is /my/path/project/static and the other is /my/path/project/jsutils/static.
I'm having a hard time getting the webserver to look in both directories for static content. Here is my entry for static location in the nginx configuration file for my app.
location ^~ /static {
root /my/path/project/static;
alias /my/path/project/jsutils/static;
index index.html index.htm;
}
I get an error that says : "alias" directive is duplicate, "root" directive was specified earlier.
I'm not sure how to go about having nginx look in both these paths for static content.
Thank you in advance for any help.
location ^~ /static {
root /my/path/project/static;
index index.html index.htm;
try_files $uri $uri/ #secondStatic;
}
location #secondStatic {
root /my/path/project/jsutils/static;
}
So first the file will be searched in /my/path/project/static and if that could not be found there, the secondStatic location will be triggered where the root is changed to /my/path/project/jsutils/static.
You may use try_files (http://wiki.nginx.org/HttpCoreModule#try_files). Assuming that you static files are in /my/path/project/static and /my/path/project/jsutils/static. you can try this:
location ^~ /static {
root /my/path/project;
index index.html index.htm;
try_files $uri $uri/ /jsutils$uri /jsutils$uri/ =404;
}
Let me know if it works. Thanks!
Just implement your configuration in nginx language:
location /my/path/project/static {
try_files $uri =404;
}
location /my/path/project/jsutils/static {
try_files $uri =404;
}
I had the exact same problem and it looks like nginx doesn't like when root is overwritten by an alias. I fixed it by firstly removing the root declaration that was inside the server section and instead declared the root and alias appropriately directly in the location sections (note the commented out lines):
server {
# root /usr/share/nginx/html;
location /logs/ {
root /home/user/develop/app_test;
autoindex on;
}
location /logs2/ {
# root /home/user/branches/app_test;
alias /home/user/branches/app_test/logs/;
autoindex on;
}
}

Resources