How to do case insensitive regex on an Nginx server? - regex

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

Related

Nginx configuration skipping if statement in parent location block

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

Elastic beanstalk nginx redirect setting not working

I'm trying to redirect http to https and I have this config:
files:
"/etc/nginx/conf.d/robots.conf":
mode: "000544"
owner: root
group: root
content: |
server {
listen 80;
server_name example.com;
location =/health {
return 200 "health-check";
}
location / {
if ($http_x_forwarded_proto != "https") {
return 301 https://$host$request_uri;
}
}
}
server {
listen 80 default_server;
server_name www.example.com;
location =/health {
return 200 "health-check";
}
location / {
if ($http_x_forwarded_proto != "https") {
return 301 https://$host$request_uri;
}
}
}
But when I go to http://example.com or http://www.example.com there is no redirect...
I've tried multiple settings but it seems like it never fully works.
The best way to do this is set your config as below
server {
listen 80;
server_name my.example.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl;
server_name my.example.com;
# add Strict-Transport-Security to prevent man in the middle attacks
add_header Strict-Transport-Security "max-age=31536000";
[....]
}

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.

Replace block of code surrounded by nested curly braces

I have this block of setting:
...
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
root /usr/share/nginx/html;
# Load configuration files for the default server block.
include /etc/nginx/default.d/*.conf;
location / {
}
error_page 404 /404.html;
location = /40x.html {
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
}
}
I want to replace whole server block with another block. Looking to use AWK or SED.
Replaced with
...
server {
listen 80;
listen 443 ssl;
server_name www.example.com;
ssl_certificate www.example.com.crt;
ssl_certificate_key www.example.com.key;
}
}
Regex pattern that I found working on selecting what I needed.
Using -m (Multiline) modifier. Tool used: Regex101.com
server {(?:[^}]*}[^}])* or server {[\S\s]*(?=^\s{4}\}$)\s*}
This MAY be what you want:
$ cat tst.awk
braceCnt {
if ( /{/ ) { braceCnt++ }
if ( /}/ ) { braceCnt-- }
}
!braceCnt
/^[[:space:]]*server[[:space:]]*{/ {
print newBlock
braceCnt = 1
}
$ awk -v newBlock=' listen 80;
listen 443 ssl;
server_name www.example.com;
ssl_certificate www.example.com.crt;
ssl_certificate_key www.example.com.key;' -f tst.awk file
...
server {
listen 80;
listen 443 ssl;
server_name www.example.com;
ssl_certificate www.example.com.crt;
ssl_certificate_key www.example.com.key;
}
}
or if you prefer relying on indentation/white space than counting braces, this would work too:
$ cat tst.awk
inBlock && /^ {4}}/ { inBlock=0 }
!inBlock
/^[[:space:]]*server[[:space:]]*{/ { print newBlock; inBlock=1 }
or if you just want the number of spaces before the closing brace to match the number before server rather than hard-coding it to 4:
$ cat tst.awk
inBlock && ($0 ~ "^[[:space:]]{"indent"}}") { inBlock=0 }
!inBlock
/^[[:space:]]*server[[:space:]]*{/ {
indent = index($0,"s") - 1
print newBlock; inBlock=1
}
etc., etc.... It all depends on your requirements....
select } that has four spaces before it and nothing behind on same line :
/^\s{4}\}$/m

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.. )