Nginx configuration skipping if statement in parent location block - if-statement

I have the following setup:
location #public {
auth_basic off;
}
location #webdav {
proxy_set_header Host $host;
proxy_pass http://localhost:8080;
}
location / {
# WebDAV server
if ($request_method != GET) {
error_page 418 = #webdav;
return 418;
}
gzip on;
fancyindex on;
location ~ /(public|\.well-known)/ {
if ($remote_user = "") {
error_page 418 = #public;
return 418;
}
}
location = /robots.txt {
add_header Content-Type text/plain;
return 200 "User-agent: *\nDisallow: /\n";
}
}
I want to redirect every non-GET request to my internal WebDAV handler written in Go. The /public folder should be accessible without basic auth, unlike the rest of the filesystem.
However, for nested location blocks, my parent if statement seems to be ignored. Attempting any non-GET request on /public results in 505, and on /robots.txt returns my configured text. However, this is not the case with gzip or fancyindex, as both /public and robots.txt are gzipped and fancy indexed.

Turns out the problem was the order in which nginx processes blocks. The two nested location blocks are executed first, and only then the parent location statements. Since my nested location blocks both return, the parent location statements are never reached. The solution was to add my WebDAV code to the beginning of each of these nested location blocks. Final code:
location #public {
auth_basic off;
}
location #webdav {
proxy_set_header Host $host;
proxy_pass http://localhost:8080;
}
location / {
# WebDAV server
if ($request_method != GET) {
error_page 418 = #webdav;
return 418;
}
gzip on;
fancyindex on;
location ~ /(public|\.well-known)/ {
# WebDAV server
if ($request_method != GET) {
error_page 418 = #webdav;
return 418;
}
if ($remote_user = "") {
error_page 418 = #public;
return 418;
}
}
location = /robots.txt {
# WebDAV server
if ($request_method != GET) {
error_page 418 = #webdav;
return 418;
}
add_header Content-Type text/plain;
return 200 "User-agent: *\nDisallow: /\n";
}
}

Related

Config nested location NGINX

Now I config my location in Nginx:
server {
location / {
include proxy_params;
proxy_pass http://unix:/run/gunicorn.sock;
}
}
I want to change it into nested location:
http://exp.com/api instead of the current: http://exp.com/
I tried but it's not success:
server {
location / {
location /api {
include proxy_params;
proxy_pass http://unix:/run/gunicorn.sock;
}
}
}
location does not need to be nested.
Have you tried the following?
server {
location /api {
include proxy_params;
proxy_pass http://unix:/run/gunicorn.sock;
}
}

Nginx Rule to redirect one specific server_name

I have an nginx with multiples server_name, and need redirect to https a specific server_name.
With my configuration (below), i got error ERR_TOO_MANY_REDIRECTS
My conf:
# /etc/nginx/sites-available/k2cloud_staging
upstream puma_k2cloud_staging {
server unix:/home/outracoisa/k2cloud/shared/tmp/sockets/puma.sock fail_timeout=0;
}
server {
listen 8080;
client_max_body_size 4G;
keepalive_timeout 10;
error_page 500 502 504 /500.html;
error_page 503 #503;
server_name contoso.com www.contoso.com contoso.com.br www.contoso.com.br contoso.sapo.pt www.contoso.sapo.pt;
root /home/outracoisa/k2cloud/current/public;
try_files $uri/index.html $uri #puma_k2cloud_staging;
if ($host ~* www\.(.*)) {
set $host_without_www $1;
rewrite ^(.*)$ http://$host_without_www$1 permanent;
}
rewrite ^/rio/?$ http://contoso.com/rio/pt-BR permanent;
rewrite ^/lisboa/?$ http://contoso.sapo.pt/lisboa/pt-PT permanent;
if ($host ~ contoso.com(\.br)) {
rewrite ^/?$ http://contoso.com/rio/pt-BR permanent;
}
if ($host = contoso.sapo.pt) {
rewrite ^/?$ http://contoso.sapo.pt/lisboa/pt-PT permanent;
}
location ~ ^/(rio|lisboa) {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://puma_k2cloud_staging;
# limit_req zone=one;
access_log /home/outracoisa/k2cloud/shared/log/nginx.access.log;
error_log /home/outracoisa/k2cloud/shared/log/nginx.error.log;
}
########## Ramos #####
if ($scheme != "https") {
set $a x;
}
if ($host = contoso.sapo.pt) {
set $a "${a}y";
}
if ($a = xy) {
rewrite ^(.*) https://$host$1 permanent;
break;
}
################
location ^~ /assets/ {
gzip_static on;
expires max;
add_header Cache-Control public;
}
location = /50x.html {
root html;
}
location = /404.html {
root html;
}
location #503 {
error_page 405 = /system/maintenance.html;
if (-f $document_root/system/maintenance.html) {
rewrite ^(.*)$ /system/maintenance.html break;
}
rewrite ^(.*)$ /503.html break;
}
if ($request_method !~ ^(GET|HEAD|PUT|PATCH|POST|DELETE|OPTIONS)$ ){
return 405;
}
if (-f $document_root/system/maintenance.html) {
return 503;
}
location ~ \.(php|html)$ {
return 405;
}
}
Anyone can help me to solve it please?
I think you do it very complicated. First: If is evil :)
As I see you want to do several things:
remove the www "prefix"
/rio and /lisboa: use a specific host
contoso.com and contoso.sapo.pt: use a specific directory
contoso.sapo.pt: force https
I would do it another way:
Use another server sections with server_name www.contoso.com, server_name www.contoso.com.br and return 301 $scheme://....
Try location /rio and location /lisboa! Or maybe better solution is to use alias (I think your nginx servers static contents and the /rio/pt-BR is a directory on your server).
Use server blocks!
Again in server block! return 301 https://$host$request_uri;
I think it would be easier maintain and debug if you create small blocks not only big one with many if and rewrite.

How to do case insensitive regex on an Nginx server?

A serious problem happened after migrating a server from Ubuntu to Debian. Debian won't allow two files, for example "a.html" and "A.html" to be in the same directory.
My server gets three types of requests and this is the current status:
requests such as /archive/2014/www.Test.com are supplied with the file: /archive/2014/blank.html
requests such as /archive/2015/Test.com and /archive/2015/www.Test.com are supplied with the file /archive/2015/T.html
requests such as /archive/2015/test.com and /archive/2015/www.test.com are supplied with the file /archive/2015/t.html
I want the last two types of requests to supply the file /archive/2015/t.html in both cases (in an case insensitive way).
How could I achieve this outcome?
The current server settings are:
server {
listen 127.0.0.1:80;
server_name 127.0.0.1;
access_log /srv/siteone/logs/access.log;
error_log /srv/siteone/logs/error.log error;
location / {
root /srv/siteone/html;
index index.html index.htm;
expires 1d;
}
rewrite ^/archive/2014/(.+)$ /archive/2014/blank.html last;
rewrite ^/archive/2015/(www\.)*(.)(.+)$ /archive/2015/$2.html last;
error_page 403 /403.html;
error_page 404 /404.html;
}
There are plenty of ways you can go about this problem.
Since you need to change only one specific letter to lowercase, you could use "map" with case insensitive regular expression:
map $request $letter {
"~*^/archive/[0-9]{4}/(www\.)?a(.*)?$" a;
"~*^/archive/[0-9]{4}/(www\.)?b(.*)?$" b;
"~*^/archive/[0-9]{4}/(www\.)?c(.*)?$" c;
"~*^/archive/[0-9]{4}/(www\.)?d(.*)?$" d;
"~*^/archive/[0-9]{4}/(www\.)?e(.*)?$" e;
"~*^/archive/[0-9]{4}/(www\.)?f(.*)?$" f;
"~*^/archive/[0-9]{4}/(www\.)?g(.*)?$" g;
"~*^/archive/[0-9]{4}/(www\.)?h(.*)?$" h;
"~*^/archive/[0-9]{4}/(www\.)?i(.*)?$" i;
"~*^/archive/[0-9]{4}/(www\.)?j(.*)?$" j;
"~*^/archive/[0-9]{4}/(www\.)?k(.*)?$" k;
"~*^/archive/[0-9]{4}/(www\.)?l(.*)?$" l;
"~*^/archive/[0-9]{4}/(www\.)?m(.*)?$" m;
"~*^/archive/[0-9]{4}/(www\.)?n(.*)?$" n;
"~*^/archive/[0-9]{4}/(www\.)?o(.*)?$" o;
"~*^/archive/[0-9]{4}/(www\.)?p(.*)?$" p;
"~*^/archive/[0-9]{4}/(www\.)?q(.*)?$" q;
"~*^/archive/[0-9]{4}/(www\.)?r(.*)?$" r;
"~*^/archive/[0-9]{4}/(www\.)?s(.*)?$" s;
"~*^/archive/[0-9]{4}/(www\.)?t(.*)?$" t;
"~*^/archive/[0-9]{4}/(www\.)?u(.*)?$" u;
"~*^/archive/[0-9]{4}/(www\.)?v(.*)?$" v;
"~*^/archive/[0-9]{4}/(www\.)?w(.*)?$" w;
"~*^/archive/[0-9]{4}/(www\.)?x(.*)?$" x;
"~*^/archive/[0-9]{4}/(www\.)?y(.*)?$" y;
"~*^/archive/[0-9]{4}/(www\.)?z(.*)?$" z;
}
server {
listen 127.0.0.1:80;
server_name 127.0.0.1;
access_log /srv/siteone/logs/access.log;
error_log /srv/siteone/logs/error.log error;
root /srv/siteone/html;
location / {
index index.html index.htm;
expires 1d;
}
rewrite ^/archive/2014/(.+)$ /archive/2014/blank.html last;
rewrite ^/archive/2015/(www\.)?(.)(.+)$ /archive/2015/$letter.html last;
error_page 403 /403.html;
error_page 404 /404.html;
}
If you have Embedded Perl module installed (sudo apt-get install nginx-extras), you could use Perl to get the request line into lowercase:
perl_set $uri_lowercase 'sub {
my $r = shift;
return lc($r->uri);
}';
server {
listen 127.0.0.1:80;
server_name 127.0.0.1;
access_log /srv/siteone/logs/access.log;
error_log /srv/siteone/logs/error.log error;
root /srv/siteone/html;
location / {
index index.html index.htm;
expires 1d;
}
rewrite ^/archive/2014/(.+)$ /archive/2014/blank.html last;
rewrite ^/archive/2015/(www\.)?(.)(.+)$ $uri_lowercase;
rewrite ^/archive/2015/(www\.)?(.)(.+)$ /archive/2015/$2.html last;
error_page 403 /403.html;
error_page 404 /404.html;
}
If you prefer Lua before Perl, you could do the same with Lua (again, you will need nginx-extras installed):
server {
listen 127.0.0.1:80;
server_name 127.0.0.1;
access_log /srv/siteone/logs/access.log;
error_log /srv/siteone/logs/error.log error;
root /srv/siteone/html;
location / {
index index.html index.htm;
expires 1d;
}
rewrite ^/archive/2014/(.+)$ /archive/2014/blank.html last;
rewrite_by_lua 'ngx.req.set_uri(string.lower(ngx.var.uri), false)';
rewrite ^/archive/2015/(www\.)?(.)(.+)$ /archive/2015/$2.html last;
error_page 403 /403.html;
error_page 404 /404.html;
}
If you don't like all of the above, there is always some dark Nginx trickery that could help (but I really don't recommend it):
server {
listen 127.0.0.1:8484;
access_log off;
rewrite ^.*$ /archive/2015/$host.html;
root /srv/siteone/html;
location / {
index index.html index.htm;
expires 1d;
}
}
server {
listen 127.0.0.1:80;
server_name 127.0.0.1;
access_log /srv/siteone/logs/access.log;
error_log /srv/siteone/logs/error.log error;
root /srv/siteone/html;
location / {
index index.html index.htm;
expires 1d;
}
location ~* ^/archive/2015/(?<letter>[A-Z])\.html$ {
proxy_set_header Host $letter;
proxy_pass http://127.0.0.1:8484;
}
rewrite ^/archive/2014/(.+)$ /archive/2014/blank.html last;
rewrite ^/archive/2015/(www\.)?(.)(.+)$ /archive/2015/$2.html last;
error_page 403 /403.html;
error_page 404 /404.html;
}

Django+nginx serving private static files

I'm trying to server protected user-files from nginx and django.
nginx.conf:
server {
listen 80;
gzip off;
expires off;
location /static/ {
add_header X-Static hit;
autoindex on;
expires off;
root /Users/andrewshkovskii/workspace/ip_pbx/;
}
location / {
proxy_pass http://localhost:8000;
rewrite ^/audiofiles/get/(\d+)/ /audiofiles/serve/$1/ last;
}
location /media/audio {
internal;
root /var/ip_pbx/users;
}
}
Django view:
def get(self, request, *args, **kwargs):
audio_file = self.get_object()
ogg_file_version = audio_file.audiofileversion_set.filter(format="ogg")
if ogg_file_version.exists():
ogg_file_version = ogg_file_version[0]
res = HttpResponse()
res["Content-type"] = "audio/ogg"
res["X-Accel-Redirect"] = ogg_file_version.file.path
res["Content-length"] = ogg_file_version.file.size
return res
return Http404()
If I trying to GET , let's say, localhost/audiofiles/get/74/ , nginx will rewrite it to view (/audiofiles/serve/74/), and view will return response, but it respose, when returned, will trying to GET localhost:8000/%full_file_path% .. Why? (file exists..)
soled by config :
server {
listen 80;
gzip off;
expires off;
error_log /usr/local/Cellar/nginx/1.2.5/logs/error.log debug;
location /static/ {
add_header X-Static hit;
autoindex on;
expires off;
root /Users/andrewshkovskii/workspace/ip_pbx/;
}
location / {
proxy_pass http://localhost:8000;
rewrite ^/audiofiles/get/(\d+)/ /audiofiles/serve/$1/ last;
}
location ~ /var/ip_pbx/users_andrewshkovskii/(\d+)/(audio|calls)/(.*) {
internal;
root /;
}
}
Note the re for location of private files (in my case.. )

nginx subdomain and domain rewrite w proxy pass

I need these two types of rewrites:
subdomain.domain.com => domain.com/website/subdomain
otherdomain.com => domain.com/userdomain/otherdomain.com
My problem is that I want the user to see subdomain.domain.com, and otherdomain.com, not the redirected version. My current rewrite in nginx works, but the user's URL shows the rewrite, and I want this to be transparent to the user, any ideas?:
upstream domain_server { server localhost:8000 fail_timeout=0; }
server {
listen 80;
root /var/www/domain.com;
server_name domain.com ~^(?<subdomain>.*)\.domain\.com$ ~^(?<otherdomain>.*)$;
if ( $subdomain ) {
rewrite ^ http://domain.com/website/$subdomain break;
}
if ( $otherdomain ) {
rewrite ^ http://domain.com/userdomain/$otherdomain break;
}
location / {
proxy_redirect off;
proxy_buffering off;
proxy_set_header Host $http_host;
proxy_set_header X-forwarded-for $proxy_add_x_forwarded_for;
if (!-f $request_filename) {
proxy_pass http://domain_server;
break;
}
}
}
With nginx you don't need rewrites at all.
upstream domain_server { server localhost:8000 fail_timeout=0; }
proxy_set_header Host domain.com;
proxy_set_header X-forwarded-for $proxy_add_x_forwarded_for;
server {
listen 80 default_server;
location / {
proxy_pass http://domain_server/userdomain/$http_host;
}
}
server {
listen 80;
server_name domain.com;
root /var/www/domain.com;
location / {
try_files $uri #backend;
}
location #backend {
proxy_pass http://domain_server;
}
}
server {
listen 80;
server_name ~^(?<subdomain>.+)\.domain\.com$;
location / {
proxy_pass http://domain_server/website/$subdomain$request_uri;
}
}
http://nginx.org/r/proxy_pass
http://wiki.nginx.org/IfIsEvil
http://wiki.nginx.org/Pitfalls
http://nginx.org/en/docs/http/converting_rewrite_rules.html