Switching between nginx configurations (profiles)? - nginx

Given the following nginx configuration...
upstream myupstream {
server localhost:9000;
}
location ~ ^/.* {
root /path/to/var ;
try_files $uri $uri/ /index.html =404;
# proxy_pass http://myupstream;
# proxy_redirect off;
...
}
I want to flip between proxied (using a node server at port 9000) and served (at /path/to/var) easily without having to comment out each block. Is there any tool or native solution to switch between different nginx configurations? (A solution would require using nginx -s reload)

Ok, try this. Request /anyurl/?test=1 for switch to proxy, /anyurl/?test=0 for switch back to local disk.
root /path/to/var;
error_page 464 = #proxy;
location / {
# check if test argument found in request
if ($arg_test ~ ^\d+$) {
# set cookie and redirect to same location
add_header Set-Cookie "test=$arg_test";
return 302 $uri;
}
if ($cookie_test = "1") {
# cookie "test" found, redirect to named location via custom error code
# it can be any unused http code same as in error_page directive
return 464;
}
try_files $uri $uri/ /index.html =404;
}
location #proxy {
proxy_pass http://myupstream;
}

Related

nginx location empty path

I am trying to define an nginx server configuration as following, but the exact location is never triggered only the general path is triggered. So, the response header is always Access-Control-Allow-Origin. What I want is when I visit "curl -v localhost:8081" it should return me with XYZ header only, because it is exact match, but instead it returns the general one. What am I doing wrong?
server {
listen 8081;
server_name localhost;
#index index.html;
root /usr/share/nginx/wallet;
location / {
try_files $uri /index.html /example.html =404;
add_header Access-Control-Allow-Origin *;
}
location = / {
add_header XYZ "xyxxxxxxxxxxxxx";
}
}
The exact match location is the first location to process the request, but is not the final location to process the request.
Nginx internally rewrites the URI to /index.html and processed this new request in the general location block.
You can force Nginx to process the entire request within the exact match location by adding a try_files statement.
For example:
location = / {
try_files /index.html =404;
add_header XYZ "xyxxxxxxxxxxxxx";
}

Nginx config for hosting two nested websites

I am trying to write a Nginx config so that:
If the URL is exactly /Site/ it must serve C:\Site\index.html
If the URL is /Site/something/else it must serve C:\Site\something\else (fallback to index.html if it doesn't exists) BUT only if /something/ is different from /api/ and /dev/
If the URL is /Site/api/something/else it must redirect the exact request to another server otherserver/api/something/else
If the URL is exactly /Site/dev/ it must serve C:\Site-Dev\index.html
If the URL is /Site/dev/something/else it must serve C:\Site-Dev\something\else (fallback to index.html if it doesn't exists) BUT only if /something/ is different from /api/
If the URL is /Site/dev/api/something/else it must redirect the exact request to another server otherserverdev/api/something/else
So the key point here is that:
/Site/ and /Site/dev are two different websites
Each one has its own .../api endpoint
So far I wrote this config:
server {
listen 19001;
include mime.types;
location ^~ /Site/api/ {
proxy_pass http://otherserver;
}
location ^~ /Site/dev/api/ {
proxy_pass http://otherserverdev;
}
location = /Site/ {
root C:\Site;
try_files /index.html =404;
}
location ~ ^/Site/(.*) {
root C:\Site;
try_files $1 $1/ /index.html =404;
}
location = /Site/dev/ {
root C:\Site-Dev;
try_files /index.html =404;
}
location ~ ^/Site/dev/(.*) {
root C:\Site-Dev;
try_files $1 $1/ /index.html =404;
}
}
This doesn't work at all, I'm getting index.html in every request and also dev and nondev are mixed up.
Is there a way to achieve what I need?

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 if host true with location and root

I use docker with nginx and this is my app config file:
server {
listen 80;
server_name app.me.site;
return 308 https://$host$uri;
location .well-known {
root /var/www/.well-known;
try_files /$uri /$uri/ /index.html;
}
# other configs
}
The path is /var/www/app.
I also created /var/www/.well-known for Let's Encrypt and it is accessible but it's only accessible for https.
I need to have an if cluse: if URL is app.me.site/.well-known, do not use https.
I tried to reach this but did not find any clue.
Your config is not workable because the return directive is executed at the NGX_HTTP_SERVER_REWRITE_PHASE while proper location selection will be done later at the NGX_HTTP_FIND_CONFIG_PHASE (request processing phases are described in the development guide). To fix it you should move that return directive to the location block (I also suggest to use the $request_uri variable instead the normalized $uri one):
location / {
# everything not started with the '/.well-known/' prefix will be processed here
return 308 https://$host$request_uri;
}
location /.well-known/ {
# do not append '/.well-known' suffix here!
# (see the difference between 'root' and 'alias' directives)
root /var/www;
try_files $uri =404;
}

Location not working for files but only for path

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;
}
...
}

Resources