Nginx config dynamic $host ENV variable - if-statement

I am looking for a better way to set Environment Variable based on dynamic $host value in Nginx config. What regex expression could be used?
For example, $preferred_hostid has different value based on the $host. I can use hard code if statement to set their value. However, there are so many host values.
client1.shop.abc.com
client2.shop.abc.com
clientN.shop.abc.com
server{
listen 80 default_server;
set $preferred_hostid '.abc.com';
if ($host = 'client1.shop.abc.com'){
set $preferred_hostid '.xyz.com'
}
if ($host = 'client2.shop.abc.com'){
set $preferred_hostid '.lmn.com'
}
if ($host = 'client3.shop.abc.com'){
set $preferred_hostid '.hij.com'
}
}
What is the better way to handle this scenario? Thanks

Related

How to make a conditional proxy_pass within NGINX

I am running our front and backend on K8s, this question is regarding our front-end setup using NGINX.
I am trying to find a way to gather metrics of my main front-end container (nginx + front-end) using a sidecar container that adjusts the stub_status running on /stub_status on port 8080 values to key value pairs for my application. Just so that I can scrape them using Prometheus on the sidecars /metrics on port 9113.
I want to block traffic for stub_staus outside of the pod, as only the sidecar needs to be able to reach it and I am trying to block /metrics from outside the platform (hence an example random 172 range address as an example). If I instead of a proxy pass (see below) use return 444 or 404 I get a nice big fat error. However, our front-end can handle 404 in a nice (graceful) way making it so you do not even exit the front-end but simply get a user friendly 404 message whilst staying in the front-end application, and end up on location/404. This also makes it appear like there is nothing on the /stub_status or /metrics. Which is nice to have.
I tried to do a rewrite ^/metrics$ to a /404 (for example) but that simply got me an NGINX 404 instead. Maybe it has something to do with the fact that /metrics runs on 9113 and there is nothing listening tot /404 on 9113. I am not sure about this.
I know the proxy pass example below is not possible within an IF statement, as I get the following error "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 in". However, below is to illustrate what I am trying to do. I am just not sure how to get it to behave like this.
Thanks in advance!
HTTP Block
http {
#Block traffic from outside platform for /stub_status
map $remote_addr $block_outside_pod {
default 1;
127.0.0.1 0;
}
#Block traffic from outside platform for /metrics
map $http_x_forwarded_for $block_outside_platform {
default 1;
~^172\.30\.\d*.\d*$ 0;
}
}
Server Block
(What I am trying to accomplish)
server {
location /stub_status {
#sidecar only
if ($block_outside_pod = 1) {
proxy_pass http://localhost:8080/404;
}
stub_status;
}
location /metrics {
#platform only
if ($block_outside_platform = 1) {
proxy_pass http://localhost:8080/404;
}
}
}
After more testing I found out the following works really well:
HTTP BLOCK
http {
#Block traffic from outside platform for /stub_status
map $remote_addr $block_outside_pod {
default 1;
127.0.0.1 0;
::1 0;
}
#Block traffic from outside platform for /metrics
map $http_x_forwarded_for $block_outside_platform {
default 1;
~^172\.30\.\d*.\d*$ 0;
}
}
SERVER BLOCK
server {
listen 8080 default_server;
server_name _;
root /<insert your location>;
error_page 418 = #block_traffic;
location /stub_status {
#sidecar only
if ($block_outside_pod = 1) {
return 418;
}
stub_status;
}
error_page 418 = #block_traffic;
location /metrics {
#platform only
if ($block_outside_platform = 1) {
return 418;
}
}
location #block_traffic {
rewrite ^ /404$1;
proxy_pass http://localhost:8080;
}
location / {
index.html etc...
add_header etc...
}
This means that anything coming in on 8080 via k8s to this pod from outside the platform will get a nice fancy 404 page from our website making it seem like it is not there =)
Thanks for the headsup from the person in the NGINX Slack!

Nginx Access Log Name Based on Non-WWW Host

So,
I've got a server with around 30 virtual host configurations, each in their own separate file. My main aim at this point is to name the access log based on the $host variable.
At the moment, I'm using the following, inside of my HTTP block to be applied to all conf files:
http {
access_log /var/log/nginx/$host.access.log
}
I'd like to be able to rewrite the above $host without the www., and just keep the domain itself. I've found the following solution for that:
if ($domain ~* www\.(.*)) {
set $domain $1;
rewrite ^(.*)$ http://$domain$1 permanent;
}
Only problem is.. 'IF' Directives are not allowed inside of the 'http' block.. Is there anyway I can achieve this, whilst still being within the 'http' block? Maybe using 'map'?
Thanks in advance,
Tom
You should use a map
http {
map $host $hostw {
default $host;
~*^www\.(.*) $1;
}
access_log /var/log/nginx/$hostw.access.log
}

Do i need to declare if statement in each server block

I have a bot blocking map that I am using that makes nginx respond with 444. I have "if" blocks in each server block and it works, but i want to know if just putting it at the top in its own server block would be better, or how exactly should i be doing it?
Currently, my domainname.org.conf file has 3 server blocks. The first block does insecure and secure redirect from old domain requests to the new domain (we rebranded). The second block handles redirecting domainname.org to www.domainname.org. The third block actually has the meat and potatoes for PHP and whatnot.
here is the conf, with some data omitted and obfuscated.
server {
listen 80;
listen 443;
MY CERT STUFF IS HERE BUT I'M NOT SHARING DETAILS
server_name olddomainname.org *.olddomainname.org;
#blocks blank user_agents
if ($limit_bots = 1) {
return 444;
}
return 301 https://www.newdomainname.org$request_uri;
}
#############################################################################################
# THIS SERVER BLOCK SIMPLY ACCEPTS ANY INCOMING NON-SUBDOMAININSECURE DOMAIN AND REROUTES IT TO A SECURE ONE
# IT ONLY AFFECTS THE PROD WEBSITE. IT SHOULD NOT INTERFERE WITH ANY DEVELOPMENT SUBDOMAINS
server {
listen 80;
listen 443;
server_name newdomainname.org;
#blocks blank user_agents
if ($limit_bots = 1) {
return 444;
}
return 301 $scheme://www.newdomainname.org$request_uri;
}
#############################################################################################
# THIS SERVER BLOCK HANDLES ANY INCOMING INSECURE SUBDOMAIN
server {
listen 80;
listen 443 default ssl;
MY CERT STUFF IS HERE BUT I'M NOT SHARING DETAILS
server_name www.newdomainname.org;
#blocks blank user_agents
if ($limit_bots = 1) {
return 444;
}
## Redirect 80 to 443
if ($scheme = http) {
return 301 https://$server_name$request_uri;
}
root /var/www;
This works, but adds repetition.
i want to know if just putting it at the top in its own server block would be better
No - it would either be ignored, or it would break your existing server blocks.
nginx chooses one server block to process a request, based on the values of the listen and server_name directives. See this document for details.
If you have a lot of common code, shared across multiple server blocks, place it in a separate file and use an include statement. See this document for more.

(Nginx) Regex pattern to match everything but www

Basically, I'm trying to setup nginx with regex so that it:
Matches all subdomains for pyronexus.com & notoriouspyro.com but NOT www (www is redirected to pyronexus.com).
Gives me a variable I can use to determine what the subdomain and domain entered are (e.g. if someone enters space.pyronexus.com, I would like to have two variables $subdomain and $domain containing space and pyronexus).
So far, I have this: ~^(.*)\.(?:pyronexus|notoriouspyro)\.com$
But I just can't seem to figure out anything else! Any help would be greatly appreciated.
Edit: Perhaps it would help to show my nginx config file:
server {
server_name pyronexus.com notoriouspyro.com;
listen 127.0.0.1:80 default_server;
root /home/nginx/pyronexus.com/public;
index index.html index.php;
access_log /home/nginx/pyronexus.com/logs/access.log;
error_log /home/nginx/pyronexus.com/logs/error.log;
include php.conf;
}
server {
server_name ~^(www\.)?(.+)$;
listen 127.0.0.1:80;
return 301 $scheme://$2$request_uri;
}
The first part is the server which I need the regex for, the second part is to try and catch all domains landing on www and redirect them without www.
This is pretty easy, like #Melvyn said, you are over thinking this, you need a catch all server to handle all domains, then create a specific server for redirecting the www.
The best variable you want to know the host you are accessing is $http_host
server {
listen 80 default_server;
# here handle all subdomains, this will also match the non-www domains of
# the both domains
}
server {
listen 80;
server_name www.pyronexus.com;
return 301 http://pyronexus.com$request_uri;
}
server {
listen 80;
server_name www.notoriouspyro.com;
return 301 http://notoriouspyro.com$request_uri;
}
This pattern seems to do it:
^((?!www).+?)\.(?:pyronexus|notoriouspyro)\.com$
Courtesy of Regular expression to match a line that doesn't contain a word?
Tested here:
http://regex101.com/r/yK7oE2/1
In case you need the domain name, simply leave out the ?::
^((?!www).+?)\.(pyronexus|notoriouspyro)\.com$
Take a step back. The task is:
serve sites on pyronexus.com and notoriouspyro.com
redirect subdomains to their respective domains
redirect www subdomains to pyronexus.com
So instead of fiddling with an overly complex regular expression, make three server blocks. The second in the task list is the catch all.

Redirecting a subdomain with a regular expression in nginx

The nginx documentation says that the server_name directive supports regular expressions. I've been banging my head against the wall trying to get even a trivial regex working.
I want http://subdomain.mydomain.com to redirect to http://mydomain.com/subdomain
Here is my code.
server {
server_name "~^subdomain\.mydomain\.com$";
rewrite ^ http://mydomain.com/subdomain;
}
Also, potentially noteworthy. Further down in the nginx config file there is a rule:
server {
server_name *.mydomain.com
...
}
What am I doing wrong?
UPDATE:
It has been suggested that I not use regex for this... to offer a little more clarity: the trivial regex was simply for purposes of troubleshooting. The real regex will look more like...
server {
server_name "~^.*(cvg|cincinnati)\.fakeairport(app)?\.(org|com)$";
rewrite ^ http://fakeairport.com/cincinnati;
}
server {
server_name "~^.*(lex|lexington)\.fakeairport(app)?\.(org|com)$";
rewrite ^ http://fakeairport.com/lexington;
}
So it would be preferable to use regex.
To answer an old question to help others
using nginx 1.1.19 you can do the following:
server {
server_name ~^(?<subdomain>\w+)\.domainA\.com$;
location / {
rewrite ^ https://$subdomain.domainB.com$request_uri permanent;
}
}
The subdomain before domainA.com is matched and stored in variable $subdomain which then can be used in the rewrite.
This rewrites url like xxx.domainA.com to xxx.domainB.com with only one server directive.
Gotta love regex with NGINX!
As I often work with multiple domain-names and I like to keep my configs as clean and rock solid as possible I almost always use regex with nginx.
In this case I've solved it with the following regex:
server {
listen 80;
server_name ~^((?<subdomain>.*)\.)(?<domain>[^.]+)\.(?<tld>[^.]+)$;
return 301 $scheme://${domain}.${tld};
}
What this does is the following: every subdomain.domain-name.tld that points to this server (ip address) is automatically redirected to domain-name.tld.
So for instance www.myexampledomain.com is redirected to myexampledomain.com.
To answer the question, what you could also do is the following:
server {
listen 80;
server_name ~^((?<subdomain>.*)\.)(?<domain>[^.]+)\.(?<tld>[^.]+)$;
return 301 $scheme://${domain}.${tld}/${subdomain};
}
Now mysubdomain.myexampledomain.com is converted into myexampledomain.com/mysubdomain.
Above regex is great as you can throw anything at it that you like and it will convert it for you.
If you read the server_name matching rules, you'll see that prefix and suffix server_names are checked before regex names, but after exact hostnames. Since *.mydomain.com matches, the regex isn't tested. The fact that it's listed earlier in the config makes no difference. Since you're just trying to match a single hostname with your regex, a simple:
server {
server_name subdomain.mydomain.com;
rewrite ^ http://mydomain.com/subdomain$request_uri?;
}
server {
server_name *.mydomain.com;
# stuff
}
will work for you.
I know everyone is saying if is evil in nginx config files, but sometimes you can't get around any other way.
server {
server_name .mydomain.com;
if ( $host ~ subdomain.mydomain.com ) {
rewrite ^(.*) http://mydomain.com/subdomain$1;
}
}
Just as a comment. If you want to redirect all subdomain levels to first subdomain level, util when you use a wildcard SSL certificate for example, you could use:
server {
listen 80;
server_name ~^(.*)\.(?<subdomain>\w+).mydomain\.com$;
return 301 https://$subdomain.mydomain.com$request_uri;
}
server {
listen 80;
server_name ~^(?<subdomain>\w+).mydomain\.com$;
return 301 https://$subdomain.mydomain.com$request_uri;
}
The first is for redirect an http multiple level subdomain to the first subdomain level in https. And the next is for redirect the first level subdomain in http to the same subdomain in https.