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

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.

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?

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.

nginx try_files without any files - internal redirect

Given:
location ~ /foo/ {
try_files $uri $uri/ /foohandler.py;
}
try_files: "If none of the files were found, an internal redirect to the uri specified in the last parameter is made" 1.
If I know $uri and $uri/ will never exist, how do I always do an internal redirect to /foohandler.py without using try_files?
Using try_files /foohandler.py is invalid syntax. What is the proper equivalent? return? rewrite?
location ~ /foo/ {
rewrite ^ /foohandler.py break;
}
You can still use try_files. It requires more than one parameter, so just add a 404 code to avoid the syntax error:
location ~ /foo/ {
try_files /foohandler.py =404;
}
On a side note, if your uri always begins with /foo/ then a prefix location would be better:
location /foo/ {
try_files /foohandler.py =404;
}

Nginx - multiple/nested IF statements

What i want to do:
Check if request comes from Facebook
Check if URL is like domain.com/2
If above conditions are true - show content from /api/content/item/$1?social=1
If above conditions are false - show "normal page"
It is a single page app. Before my changes configuration looked like this (and it worked):
location / {
root /home/eshlox/projects/XXX/project/project/assets/dist;
try_files $uri $uri/ /index.html =404;
}
I've tried to use if statements:
location / {
set $social 1;
if ($http_user_agent ~* "facebookexternalhit") {
set $social UA;
}
if ($uri ~* "^/(\d+)$") {
set $social "${social}URL";
}
if ($social = UAURL) {
rewrite ^/(\d+)$ /api/content/item/$1?social=1;
}
root /home/eshlox/projects/XXX/project/project/assets/dist;
try_files $uri $uri/ /index.html =404;
}
With this configuration everything works as i expected only if both conditions are true or false.
If one of conditions is true and the second is false (or vice versa) then nginx always returns status 404.
I have found "IfIsEvil" on nginx site, i've tried to use mapping (can i use mapping in this case?) but still i can't resolve this problem.
Any ideas?
Best regards.
There is good article about common pitfalls in nignx wiki.
First, I've moved root directive to server level. Second, location is the best way to check urls. So I rethink your requirements as
if location consist of digits
and request from facebook
we have to rewrite url, and the result is:
root /home/eshlox/projects/XXX/project/project/assets/dist;
location / {
try_files $uri $uri/ /index.html;
}
location ~ "^/\d+$" {
if ($http_user_agent ~* "facebookexternalhit") {
rewrite (.+) /api/content/item$1?social=1;
}
try_files $uri $uri/ /index.html;
}
Also, there is almost no reason to have =404 after /index.html in try_files directive.

Resources