NGinx location directive with Regex - regex

I'm looking for a way to get my NGinx LB to do the following conversion.
User access URL: https://example.com/t/foo.com/common/<something>
Proxied by LB: https://example.com/<something>?t=foo.com
First of all, is this possible to be done?
I tried the below config but didn't work as expected.
server {
listen 443;
server_name example.com;
ssl on;
ssl_certificate /etc/ssl/certs/example-selfsigned.crt;
ssl_certificate_key /etc/ssl/private/example-selfsigned.key;
location /t/[a-z.]+/common/ {
rewrite ^(/t/.*)/common/(.*)$ https://192.168.1.3:9443/$2?t=$1 break;
proxy_pass https://192.168.1.3:9443/;
}
}
Any help on this would be greatly appreciated!

Regex matching locations should use ~ (or ~* for case-insensitive matching) modifiers. You should not add the scheme or domain part to your rewrite directive when you want to rewrite an URI that to be proxied with proxy_pass directive. Try the following:
location ~ ^/t/(?<t>[a-z.]+)/common(?<path>/.*) {
rewrite ^ $path?t=$t break;
proxy_pass proxy_pass https://192.168.1.3:9443;
}

Related

Using nginx proxy to serve static S3 to a different bucket for bots like googlebot

Using nginx to proxy subdomains of superduper.io to a specific folder in S3 bucket. The bucket has a folder for each subdomain that holds a static HTML website.
Now I want to redirect bots to a different S3 bucket. What do I need to add to my nginx.conf file?
worker_processes auto;
# include /etc/nginx/modules-enabled/*.conf;
events {
worker_connections 768;
# multi_accept on;
}
http {
server {
listen 80;
server_name *.superduper.io;
access_log /var/log/nginx/superduper.access.log;
error_log /var/log/nginx/superduper.error.log;
location / {
resolver 8.8.8.8;
set $bucket "https://superduper-spa.s3.us-east-1.amazonaws.com:443";
rewrite ^([^.]*[^/])$ $1/ permanent;
# matches: subdomain.superduper.io
if ($host ~ ^([^.]*)\.superduper\.io) {
set $subdomain $1;
proxy_pass https://$bucket/${subdomain}${uri};
}
proxy_intercept_errors on;
proxy_redirect off;
proxy_set_header Host $bucket;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_hide_header x-amz-id-2;
proxy_hide_header x-amz-request-id;
}
}
# include /etc/nginx/conf.d/*.conf;
# include /etc/nginx/sites-enabled/*;
}
As nginx doesn't support an else statement, nor an if with more than a single condition, it may get tricky to write certain configurations that depend on multiple variables.
For your usecase, there are two improvements for a potential solution, which can be used either together or apart:
You might want to convert your existing if with a regex around $host into a named capture within server_name as per http://nginx.org/r/server_name. This way, you'll then be free to use whatever transformation you require around $http_user_agent matching against the bots, e.g., by using an if statement more like an actual conditional than just as a way to capture the subdomain.
-server_name *.example.su;
-if ($host ~ ^([^.]*)\.example\.su$) {
- set $subdomain $1
-}
+server_name ~^(p<subdomain>[^.]*)\.example\.su$;
You might want to make use of http://nginx.org/r/map. It allows to map any inputs to an output. You can combine a match for both $host and $http_user_agent to make the transformation.
map $http_host/$http_user_agent $bucket {
"~^(p<subdomain>[^.]*)\.example\.su.*bot.*$" $bucketPrefix/$subdomain/bot;
"~^(p<subdomain>[^.]*)\.example\.su.*$" $bucketPrefix/$subdomain;
default $bucketPrefix;
}
server {
…
proxy_pass $bucket$uri;
}
Alternative solution would be to use Cloudfront in front of S3, then use a Lamdbda at edge function to redirect specific user-agents to a different bucket without having to touch nginx.

Nginx regex and wildcard for location

I am stuck with one issue. I am trying to hit
http://localhost/api/hello/somename
Now somename could be anything sam or phil,
now my config file of nginx is below.
server {
listen 80 default_server;
server_name _;
location ~ ^/api/(.*)$ {
proxy_pass http://localhost:8081/api/hello/$1;
}
}
Where I am wrong ? Can you pls help me to fix. Actually on 8081 container is running.
You don't need to make it this complex. You could just it as simple as below
server {
listen 80 default_server;
server_name _;
location /api/ {
proxy_pass http://localhost:8081/api/;
}
}
And that should pass anything starting with /api to http://localhost:8081/api/, with the request uri after /api/ appended
/ should be escaped
.* is anything. If you know there should be names and / between them say it
^https?:\/\/\w+\/api(?:\/\w+)*$
http or https then : then //, then a word, then /api, then maybe some /word
test

Force WWW behind an AWS EC2 Load Balancer

I've come up with a small issue, we're using a load balancer for a new project, but we cannot force the www. without having a redirect loop between requests.
We're currently using NGINX, and the snippet to redirect is the following:
LOAD BALANCER NGINX CONFIG
# FORGE CONFIG (DOT NOT REMOVE!)
include forge-conf/mywebsite.com/before/*;
# FORGE CONFIG (DOT NOT REMOVE!)
include upstreams/mywebsite.com;
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name .mywebsite.com;
if ($host !~* ^www\.){
rewrite ^(.*)$ https://www.mywebsite.com$1;
}
# FORGE SSL (DO NOT REMOVE!)
ssl_certificate /etc/nginx/ssl/mywebsite.com/225451/server.crt;
ssl_certificate_key /etc/nginx/ssl/mywebsite.com/225451/server.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
charset utf-8;
access_log off;
error_log /var/log/nginx/mywebsite.com-error.log error;
# FORGE CONFIG (DOT NOT REMOVE!)
include forge-conf/mywebsite.com/server/*;
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true;
proxy_pass http://370308_app/;
proxy_redirect off;
# Handle Web Socket Connections
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
# FORGE CONFIG (DOT NOT REMOVE!)
include forge-conf/mywebsite.com/after/*;
HTTP SERVER NGINX CONFIG
# FORGE CONFIG (DOT NOT REMOVE!)
include forge-conf/mywebsite.com/before/*;
server {
listen 80;
listen [::]:80;
server_name .mywebsite.com;
root /home/forge/mywebsite.com/public;
if ($host !~* ^www\.){
rewrite ^(.*)$ https://www.mywebsite.com$1;
}
# FORGE SSL (DO NOT REMOVE!)
# ssl_certificate;
# ssl_certificate_key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';
ssl_prefer_server_ciphers on;
ssl_dhparam /etc/nginx/dhparams.pem;
add_header X-Frame-Options "SAMEORIGIN";
add_header X-XSS-Protection "1; mode=block";
add_header X-Content-Type-Options "nosniff";
index index.html index.htm index.php;
charset utf-8;
# FORGE CONFIG (DOT NOT REMOVE!)
include forge-conf/mywebsite.com/server/*;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location = /favicon.ico { access_log off; log_not_found off; }
location = /robots.txt { access_log off; log_not_found off; }
access_log off;
error_log /var/log/nginx/mywebsite.com-error.log error;
error_page 404 /index.php;
location ~ \.php$ {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass unix:/var/run/php/php7.1-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
}
location ~ /\.(?!well-known).* {
deny all;
}
}
# FORGE CONFIG (DOT NOT REMOVE!)
include forge-conf/mywebsite.com/after/*;
Thing is, with this config I'm only getting redirect loops from the server.
Help please :D <3
After writing the prior general-purpose answer, I Googled "FORGE CONFIG (DOT NOT REMOVE!)", and this was the first result:
https://laracasts.com/discuss/channels/forge/forge-how-to-disable-nginx-default-redirection
inside nginx/forge-conf/be106.net/before/redirect.conf file there is this simple config:
…
server_name www.my-domain.net;
return 301 $scheme://my-domain.net$request_uri;
…
is there a simple way of removing this without altering the file itself(as it look like bad idea).
So, it appears that the redirect is being caused by the application you're using, so, we found the most likely cause of the loop!
In turn, the appropriate way to configure your application to avoid said loop would be outside of the score of StackOverflow.
However, as a workaround:
consider whether you actually need all those forge-conf include directives at the load-balancer level; subsequently, you could fake the appropriate domain to be passed to the backend that would not cause a redirect (provided you remove your own redundant redirects):
- proxy_set_header Host $http_host;
+ proxy_set_header Host example.com;
note that the reason the forge-conf/example.com/before/redirect.conf directive takes precedence over your own configuration for .example.com is the order of the directive — you could potentially move the /before/* include to be after your own configuration, if such a move would otherwise make sense.
I don't think the nginx snippets you provided would cause a redirect loop by themselves.
First, you have to figure out whether it's an actual redirect — very often in these questions, the 301 Moved Permanently response gets cached in your browser, and subsequently you see a cached version, instead of a fresh one.
Subsequently, you'd have to figure out what is causing the redirect loop:
Try adding unique strings to each redirect directive, to see which one would be causing the loop.
if ($host !~* ^www\.) {return 301 $scheme://www.$host/levelX$request_uri}
Ask yourself why do you have so many redirect directives in the first place — there doesn't seem to be much of a valid reason to have redirect directives both at the front-end load balancer, as well as the backend.
If the above doesn't resolve the issue, then you know that the redirect loop is not coming from the files you've provided, and you have to dig deeper — it's possible for it to come from some other files, perhaps one of your include directives, or perhaps a default server of www.example.com is defined elsewhere, which redirects to example.com, or perhaps the redirect is done at the application layer.

www prefix not showing site

I'm trying to make my site work with the www prefix. I can only reach the site without the prefix. I am using Nginx+Django in digitalocean. Here is my site config file:
/etc/nginx/sites-enabled/mysite
upstream app_server {
server 127.0.0.1:9000 fail_timeout=0;
}
server{
listen 80 default_server;
listen [::]:80 default_server ipv6only=on;
server_name .example.com;
root /usr/share/nginx/html;
index index.html index.htm;
client_max_body_size 4G;
keepalive_timeout 5;
# Your Django project's media files - amend as required
location /media {
alias /home/django/proyect/media;
}
# your Django project's static files - amend as required
location /static {
alias /home/django/proyect/static;
}
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://app_server;
}
}
I have tried without success these:
# rewrite ^ http://example.com$uri permanent;
# rewrite ^/(.*) http://example.com/$1 permanent;
# server_name example.com www.example.com;
What am I doing wrong?
For my site I set it up so all www traffic was sent to the non-www address of my site by having two server blocks, one to redirect www traffic to the non-www address and one the handle the non-www traffic. I think the opposite should work for your situation.
server {
listen 80;
server_name example.com;
return 301 $scheme://www.example.com$request_uri;
}
server {
listen 80;
server_name www.example.com;
...
}
I'm not sure if this is the best way to go about it as I'm still pretty new to Nginx.
The $host variable can be used to check for the occurrence of a "www" prefix. You could add this to the server section of the config to remove it:
server {
if ($host ~* www\.(.*)) {
set $host_without_www $1;
rewrite ^(.*)$ https://$host_without_www$1 permanent;
}
....
goto : DNS -> SELECT DOMAIN -> ADD RECORD -> Select record type "A"
hostname : www
IP Adress : your droplets IP

nginx sub-domain mapping

I want to open xyz.abc.com in the browser but internally(using python-django), I want to map this to abc.com/xyz
The following nginx conf code works, but I don't want to redirect the user to this new url (abc.com/xyz)
server {
listen 80;
server_name xyz.abc.com;
location / {
rewrite ^ http://abc.com/xyz;
break;
}
I have tried a lot of things including using proxy_pass but it's not working.
How can I solve this?
Thanks.
You can convert any 3rd level domain to 2nd level:
server {
listen 80;
server_name ~^(?<domain>.*)\.abc\.com;
location / {
proxy_pass http://abc.com/$domain$request_uri;
break;
}
In your case try the next:
server {
listen 80;
server_name xyz.abc.com;
location / {
proxy_pass http://abc.com/xyz$request_uri;
break;
}
About the request_uri: http://wiki.nginx.org/HttpCoreModule