Get arguments Nginx and path for proxy_pass - regex

I have this URL:
http://localhost:8888/images/upload/root/folderA/folderB?arg1=A&arg2=B
so, I want redirect all to:
http://localhost:8080/v1/files_upload/
and it must be something like:
http://localhost:8080/v1/files_upload/root/folderA/folderB?arg1=A&arg2=B
I have the following:
location ~ ^/images/upload/([^/]+)(/.*)\?(.*)$ {
upload_pass #after_upload;
...
...
}
location #after_upload {
proxy_pass http://localhost:8080/v1/files_put/$1/$2?$3;
}
I checked it, and only works $1 and $2, but the arguments $3 are not sent to proxy_pass
Thanks in advance!

The location directive doesn't match request arguments, it only checks request path. You should use the $args variable (or more specific $arg_arg1 and $arg_arg2):
location #after_upload {
proxy_pass http://localhost:8080/v1/files_put/$1/$2?$args;
}

Related

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
}

Weird redirect with proxy_pass in if statement

I've a SPA (Single Page Application) site, let's say under https://example.com and an API for it under https://api.example.com
I want to serve server rendered content for specific useragents like googlebot, facebookexternalhit, etc.
So, if user goes to https://example.com/brandon/things it will get served SPA, but if bot goes to the same URL it will get served server rendered page with all proper meta and open graph tags.
My server rendered pages with proper matching are under https://api.example.com/ssr/
So for example if bot hits https://example.com/brandon/things it should get content from https://api.example.com/ssr/brandon/things
I almost got it working with nginx proxy_pass if statement to the Django application (which returns server rendered output) but unfortunately there's one edge case that makes it behave weirdly.
My implementation:
server {
listen 80;
server_name example.com; # url of SPA
index index.html;
root /srv/example_spa/public/dist; # directory of SPA index.html
# $ssr variable that tells if we should use server side rendered page
set $ssr 0;
if ($http_user_agent ~* "googlebot|yahoo|bingbot|baiduspider|yandex|yeti|yodaobot|gigabot|ia_archiver|facebookexternalhit|facebot|twitterbot|developers\.google\.com|rogerbot|linkedinbot|embedly|quora link preview|showyoubot|outbrain|pinterest|slackbot|vkShare|W3C_Validator|redditbot") {
set $ssr 1;
}
# location block that serves proxy_pass when the $ssr matches
# or if the $ssr doesn't match it serves SPA application index.html
location / {
if ($ssr = 1) {
proxy_pass http://127.0.0.1:9505/ssr$uri$is_args$args;
}
try_files $uri /index.html;
}
}
But there's the problem:
Everything works dandy and sweet, except one case.
User hits https://example.com/brandon/things/ and he gets SPA index.html - perfect.
User hits https://example.com/brandon/things and he gets SPA index.html - perfect.
Bot hits https://example.com/brandon/things/ and he gets server rendered page - perfect.
Bot hits https://example.com/brandon/things (without appended slash) and he gets redirected (301) to https://example.com/ssr/brandon/things - BAD BAD BAD
I've tried to make it work for couple of hours now without luck.
What would you suggest? I know if in nginx is evil, but I don't know how to make it work without it...
Any help is appreciated
You need to alter the redirects for proxy_pass
location / {
proxy_redirect http://127.0.0.1/ssr/ http://$host/ssr/;
proxy_redirect /ssr/ /;
if ($ssr = 1) {
proxy_pass http://127.0.0.1:9505/ssr$uri$is_args$args;
}
try_files $uri /index.html;
}
It turns out this was issue with my Django application redirect. I thought I had "APPEND_SLASH" option disabled, but it was enabled and made redirect when there was no slash. And it redirected without changing the host to https://api.example.com, but only URI part. Hence my confusion.
And I actually found two ways to fix that.
First, just use rewrite to append slash when there isn't one.
location / {
if ($ssr = 1) {
rewrite ^([^.]*[^/])$ $1/ permanent;
proxy_pass http://127.0.0.1:9505/ssr$uri$is_args$args;
}
try_files $uri /index.html;
}
Second, modify proxy_pass to always add / slash after $uri part and server side render application url config to accept two slashes at the end //'. It's a little hacky but has no side effects and works as it should.
Nginx config:
location / {
if ($ssr = 1) {
proxy_pass http://127.0.0.1:9505/ssr$uri/$is_args$args;
}
try_files $uri /index.html;
}
Django URL regex:
r'^ssr/(?P<username>[\w-]+)/(?P<slug>[\w-]+)(/|//)$'

How can I serve nested projects in Nginx

I have a Lumen api project with multiple git tags for api versioning. So I have to deploy multiple checkouts of the project.
The folder structure on the server looks like this:
var
www
api-staging
master
v1
public
index.php
...
v2
public
index.php
...
lastest
public
index.php
...
...
Now I'd like to serve the projects via nginx so that the url looks something like this.
http://BRANCH.domain.tld/VERSION/ eg. http://master.domain.tld/lastest/
I have tried a lot with regexp, but nothing really worked. I hope you can help me out.
You will need to capture the BRANCH using a regular expression server_name statement. See this document for more.
The root is constructed by appending /public to the captured VERSION, which requires a regular expression location and an alias statement. See this document for more.
For example:
server {
...
server_name ~^(?<branch>.+)\.domain\.tld$;
location ~ ^/(?<version>[^/]+)/(?<name>.*)$ {
alias /var/www/api-staging/$branch$version/public/$name;
if (!-e $request_filename) { rewrite ^ $version/index.php last; }
location ~ \.php$ {
if (!-f $request_filename) { return 404; }
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $request_filename;
...
}
}
}

Retrieve static file from amazom s3 bucket

I am trying to configure my nginx in such a way that whenever there is some bad gateway response, I try to fetch static html contents from the s3 bucket.
The url structure of the request is some_bucket/folder1/folder2/text
And the data is stored in s3 bucket with directory structure as s3.amazonaws.com/some_bucket/folder1/folder2/folder1_folder2.html
I am not able to determine the values for folder1 and folder2 so that I can make
the html file dynamically and use proxy_pass.
Also, I tried try_files but I think that does not work for urls.
Any idea how to tackle this problem.
Thanks.
Nginx S3 proxy can handle dynamically built URL, you can also hide a directory and even part of private URL such AWS Key:
For instance the basis URL is the following:
https://your_bucket.s3.amazonaws.com/readme.txt?AWSAccessKeyId=YOUR_ONLY_ACCESS_KEY&Signature=sagw4gsafdhsd&Expires=3453445231
Resulted URL:
https://your_server/proxy_private_file/readme.txt?st=sagw4gsafdhsd&e=3453445231
The configuration is not difficult:
location ~* ^/proxy_private_file/(.*) {
set $s3_bucket 'your_bucket.s3.amazonaws.com';
set $aws_access_key 'AWSAccessKeyId=YOUR_ONLY_ACCESS_KEY';
set $url_expires 'Expires=$arg_e';
set $url_signature 'Signature=$arg_st';
set $url_full '$1?$aws_access_key&$url_expires&$url_signature';
proxy_http_version 1.1;
proxy_set_header Host $s3_bucket;
proxy_set_header Authorization '';
proxy_hide_header x-amz-id-2;
proxy_hide_header x-amz-request-id;
proxy_hide_header Set-Cookie;
proxy_ignore_headers "Set-Cookie";
proxy_buffering off;
proxy_intercept_errors on;
proxy_pass http://$s3_bucket/$url_full;
}
See the full configuration for more details.
This is what I did for someone(probably newbie) who may encounter this problem.
location ~* ^/some_bucket/(.*)/(.*)/.* {
proxy_pass http://s3.amazonaws.com/some_bucket/$1/$2/$1_$2.html;
}
~* means case insensitive regex match
^ means anything before
() for catching parameters.
For example,
User enters www.example.com/some_bucket/folder1/folder2/text
Then, it is processed as,
~* ensures case insensitive search(for case sensitive skip *(means just put ~))
^ matches www.example.com.
/some_bucket/ is matched then,
.* means any number of any character(for any numeric, replace with [0-9]*)
() ensures that matched values gets catched
So, $1 catches folder1
$2 catches folder2
Then
.* without parenthesis matches any charater but does not catch the matched value
Now the catched values can be used to find the file in amazon bucket using
proxy_pass http://s3.amazonaws.com/some_bucket/$1/$2/$1_$2.html
https://www.digitalocean.com/community/tutorials/understanding-nginx-server-and-location-block-selection-algorithms can be helpful

Add slash to the end of every url (need rewrite rule for nginx)

I try to get an / to every urls end:
example.com/art
should
example.com/art/
I use nginx as webserver.
I need the rewrite rule for this..
For better understanding check this:
http://3much.schnickschnack.info/art/projekte
If u press on a small thumbnail under the big picture it reloads and shows this url:
http://3much.schnickschnack.info/art/projekte/#0
If i now have a slash on all urls (on the end) it would work without a reload of the site.
Right now i have this settings in nginx-http.conf:
server {
listen *:80;
server_name 3much.schnickschnack.info;
access_log /data/plone/deamon/var/log/main-plone-access.log;
rewrite ^/(.*)$ /VirtualHostBase/http/3much.schnickschnack.info:80/2much/VirtualHostRoot/$1 last;
location / {
proxy_pass http://cache;
}
}
How do I configure nginx to add a slash? (I think i should a rewrite rule?)
More likely I think you would want something like this:
rewrite ^([^.]*[^/])$ $1/ permanent;
The Regular Expression translates to:
"rewrite all URIs without any '.' in them that don't end with a '/' to the URI + '/'"
Or simply:
"If the URI doesn't have a period and does not end with a slash, add a slash to the end"
The reason for only rewriting URI's without dots in them makes it so any file with a file extension doesn't get rewritten. For example your images, css, javascript, etc and prevent possible redirect loops if using some php framework that does its own rewrites also
Another common rewrite to accompany this would be:
rewrite ^([^.]*)$ /index.php;
This very simply rewrites all URI's that don't have periods in them to your index.php (or whatever file you would execute your controller from).
rewrite ^([^.\?]*[^/])$ $1/ permanent;
to avoid querystrings of a rest url getting a / tagged on.
e.g.
/myrest/do?d=12345
For nginx:
rewrite ^(.*[^/])$ $1/ permanent;
Odd that this is the first result in Google, but doesn't have a satisfactory answer. There are two good ways to do this I know of. The first is to straight-up check if the request will hit a file and only apply a rewrite condition if not. E.g.
server {
# ...
if (!-f $request_filename) {
rewrite [^/]$ $uri/ permanent;
}
location / {
# CMS logic, e.g. try_files $uri $uri /index.php$request_uri;
}
# ...
}
The second, which many prefer as they'd rather avoid any use of if that isn't 100% necessary, is to use try_files to send the request to a named location block when it won't hit a file. E.g.
server {
# ...
location / {
try_files $uri $uri/ #cms;
}
location #cms {
rewrite [^/]$ $uri/ permanent;
# CMS logic, e.g. rewrite ^ /index.php$request_uri;
}
# ...
}
it's too late but I want to share my solution, I've met issue with trailing slash and nginx.
#case :
# 1. abc.com/xyz => abc.com/xyz/
# 2. abc.com/xyz/ => abc.com/xyz/
# 3. abc.com/xyz?123&how=towork => abc.com/xyz/?123&how=towork
# 4. abc.com/xyz/?123&ho=towork => abc.com/xyz/?123&how=towork
and this is my solution
server {
....
# check if request isn't static file
if ($request_filename !~* .(gif|html|jpe?g|png|json|ico|js|css|flv|swf|pdf|xml)$ ) {
rewrite (^[^?]+[^/?])([^/]*)$ $1/$2 permanent;
}
....
location / {
....
}
}
server {
# ... omissis ...
# put this before your locations
rewrite ^(/.*[^/])$ $1/ permanent;
# ... omissis ...
}
If you want some kind of requests (say other than GET ones) to be prevented from doing this (usually it's about POST requests, as rewrite turns any request method into GET, which may break some of your site's dynamic functionality), add an if clause:
server {
# ... omissis ...
# put this before your locations
if ($request_method = "GET" ) {
rewrite ^(/.*[^/])$ $1/ permanent;
}
# ... omissis ...
}
You can also put the rewrite in a location block (if too), to make it more specific.
using the rewrites from anthonysomerset in a Wordpress, I experimented problems accesing to /wp-admin dashboard due to reirection loop. But i solve this problem using the above conditional:
if ($request_uri !~ "^/wp-admin")
{
rewrite ^([^.]*[^/])$ $1/ permanent;
rewrite ^([^.]*)$ /index.php;
}
If nginx behind proxy with https, this snippet do correct redirect for $scheme
map $http_x_forwarded_proto $upstream_scheme {
"https" "https";
default "http";
}
server {
...
location / {
rewrite ^([^.\?]*[^/])$ $upstream_scheme://$http_host$1/ permanent;
}
...
}
And on the upstream proxy pass the X-Forwarded-Proto header like:
location / {
proxy_set_header X-Forwarded-Proto $scheme;
...
}
This rule solves query string case too:
location ~ ^/([^.]*[^/])$ {
if ($query_string) {
return 301 $scheme://$host/$1/?$query_string;
}
return 301 $scheme://$host/$1/;
}
The regex has taken from #marc's answer:
rewrite ^([^.\?]*[^/])$ $1/ permanent;
The extra slash ^/ in regex is added to improve readability
Try this: ^(.*)$ http://domain.com/$1/ [L,R=301]
This redirects (Status code 301) everything ($1) without a "/" to "$1/"