nginx location empty path - nginx

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

Related

Nginx proxy_pass for '/' route only, with $http_accept

The reasoning behind this is that if a client requests Content-Type: application/ld+json from the home page of my application then the query should be redirected to an API which will serve that. So, I have something like this:
location / {
if ($http_accept = 'application/ld+json') {
proxy_pass https://api.example.org/homepage_jsonld/;
}
root /var/www/example.org/dist;
try_files $uri $uri/ /index.html;
}
...which results in:
nginx: [emerg] "proxy_pass" cannot have URI part in location given by regular expression, or inside named location, or inside "if" statement, or inside "limit_except" block
Can anyone suggest how to get around this?
Ideally, I'd only need to use the proxy_pass if the route requested is '/', and to pass any other route on to the application in /var/www/example.org/dist.
The answer turns out to be as follows:
location = '/' {
if ($http_accept = 'application/ld+json') {
rewrite "^/" /homepage_jsonld break;
proxy_pass https://api.example.org;
}
root /var/www/example.org/dist;
try_files $uri $uri/ /index.html;
}
...in addition to the existing location / block.

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

What's wrong with Nginx location match rule

I do read the Nginx documentation about location matching.
I know about the prioriy of modifier.
And here is my config.
location = /login {
root /usr/share/nginx/mysite;
try_files $uri /index.html;
}
location = / {
root /usr/share/nginx/mysite;
try_files $uri /index.html;
}
location ~ /(.*) {
proxy_pass http://127.0.0.1:8080/$1;
}
What I want is when I type "http://example.com/" "http://example.com/login" , the request will goes index.html which is a React App , and other request will goes proxy pass to my Tomcat application which is bind 8080 port.
But "http://example.com/" "http://example.com/login" request goes proxy_pass , what?
According to Nginx documentation, the "=" modifier is "Priority one"
I expect it is an exact match.
If an exact match is found, the search terminates
I also use https://nginx.viraptor.info/ test for it.
It shows what I expected.
But it looks like the running server not act what Nginx doc said.
Any ideas?
The last parameter of a try_files statement is special. It can be a status code, a named location, or the URI of an internal redirect. See this document for details.
In your current configuration, Nginx generates an internal redirection to /index.html and restarts the search for a matching location. So the /index.html request is sent to the proxy_pass.
Place /index.html before the last parameter so that it's interpreted as a file parameter and is processed within the same location block. The $uri is unnecessary in this particular case.
For example:
root /usr/share/nginx/mysite;
location = /login {
try_files /index.html =404;
}
location = / {
try_files /index.html =404;
}
location / {
proxy_pass http://127.0.0.1:8080;
}
The =404 will never be reached. The final location can be simplified and the capture is unnecessary.

Switching between nginx configurations (profiles)?

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

Resources