Nginx proxy when $http_user_agent matches mobile - nginx

I make two version of website, one for pc while the other for mobile.
The pc version runs through django which points to a reverse_proxy through http://127.0.0.1:8000/.
While the mobile version only serves the static path in /path/for/mobile.
I tried many ways, for example, but cannot get it work.
location / {
if ($http_user_agent ~ Mobile) {
root /path/for/mobile;
}
else {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
proxy_pass http://127.0.0.1:8000;
}
}

After some attempt, it turns out using extra server with fixed port number works good.
The if statement only affects a number variable, which affects the port proxy to different version of any content.
server {
# static server
listen 11112;
root /path/to/static/html;
}
server {
# dynamic server
listen 11111;
client_max_body_size 300m;
location / {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
proxy_pass http://django_devaluation:8000/;
}
}
server {
listen 80;
server_name www.example.com;
index index.html;
// here do the condition separation
set $proxy_port 11111;
if ($http_user_agent ~* mobile) {
set $proxy_port 11112;
}
location / {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
proxy_pass http://127.0.0.1:$proxy_port;
}
}

Try this - If is mobile, use the default root, otherwise proxy_pass.
location / {
set $mobile 0;
root /path/for/mobile;
if ($http_user_agent ~ Mobile) {
set $mobile 1;
}
if ($mobile = 0) {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
proxy_pass http://127.0.0.1:8000;
break;
}
}

The only safe operations on nginx if blocks are rewrite and return.
Try something like:
if ($mobile = 0) {
add_header Content-Type text/plain;
return 200 "Desktop Version";
break;
}
to verify that your $mobile variable is set correctly, then:
if ($mobile = 0) {
rewrite ^ /desktopProxy/$request_uri;
}
location ~* ^/desktopProxy/(.*)$ {
...
proxy_pass http://127.0.0.1:8000/$1$is_args$args;
break;
}
I didn't test the "$1$is_args$args" condition, verify that before using on prod.

Related

Conditionally rewrite request url based on referer - Nginx

What I am trying to achive; is when the request coming from http://<ip>/vault/ui/ (referer in the request header) and it includes the http://<ip>/v1/* endpoint, to be rewriten or redirected to http://<ip>/vault/v1/
can someone please help me to solve this issue?
/etc/nginx/sites-enabled/reverse-proxy.conf
upstream command_center_vault {
server command-center-0.blinchik.io:28200;
}
server {
listen 80;
listen [::]:80;
location /vault/ {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Accept-Encoding "";
proxy_pass "http://command_center_vault/vault/";
proxy_redirect /ui/ /vault/ui/;
}
location /vault/v1/ {
proxy_pass "http://command_center_vault/v1/";
}
}
Headers
Update
A little bit more context, the overarching architecture looks as in the picture below.
the configuration of nginx server in the private subnet looks like this:
private subnet nginx
upstream consul_server {
server brain-consul-server-0.blinchik.io:8500;
server brain-consul-server-1.blinchik.io:8500;
server brain-consul-server-2.blinchik.io:8500;
}
upstream vault_server {
server brain-vault-server-0.blinchik.io:8200;
server brain-vault-server-1.blinchik.io:8200;
}
server {
listen 28500;
listen [::]:28500;
location /consul/ {
proxy_pass "http://consul_server";
sub_filter_once off;
sub_filter_types application/javascript text/html;
sub_filter "/v1/" "/consul_v1/";
}
location /consul_v1/ {
proxy_pass "http://consul_server/v1/";
}
}
server {
listen 28200;
listen [::]:28200;
location /vault/ {
proxy_pass "http://vault_server/";
port_in_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Accept-Encoding "";
proxy_redirect /ui/ /vault/ui/;
sub_filter_once off;
sub_filter '<head>' '<head><base href="/vault/">';
sub_filter '"/ui/' '"ui/';
#inspired by this repo https://github.com/Folcky/hashicorp-vault-and-nginx
}
location /v1/ {
proxy_pass "http://vault_server/v1/";
}
}
public subnet nginx
upstream command_center_vault {
server command-center-0.blinchik.io:28200;
}
server {
listen 80;
listen [::]:80;
location /vault/ {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Accept-Encoding "";
proxy_pass "http://command_center_vault/vault/";
proxy_redirect /ui/ /vault/ui/;
}
location /vault/v1/ {
proxy_pass "http://command_center_vault/v1/";
}
}
the consul part works fine. if I change in the public subnet configuration the location of /vault/v1/ to /v1/ then it works as well. But the problem that other products that I intend to add it to the reverse proxy (like Nomad) also uses the /v1/ path and in this case there will be a conflict.
I think this one should work (must be placed at the server context outside any locations:
if ($http_referer ~ /vault/ui) {
rewrite ^/v1(/.*) /vault/v1$1 last;
}
You can make regex pattern more strict including //<ip> or https?://<ip> parts.

proxy_pass with nginx variable as argument

i want to use nginx set directive to use a variable as argument of proxy_pass but doing so ends up in a endless 301 Moved Permanently error in chrome when accessing https://foo.de/myleaps with the code below:
example code
user "reverse-proxy" "reverse-proxy";
error_log stderr;
daemon off;
events {}
http {
server {
listen 80;
listen [::]:80;
server_name foo.de;
location /myleaps {
rewrite ^ https://$server_name$request_uri? permanent;
}
} server {
ssl on;
listen 443 ssl;
listen [::]:443 ssl;
server_name foo.de;
ssl_certificate /var/lib/nixcloud/TLS/foo.de/selfsigned/fullchain.pem;
ssl_certificate_key /var/lib/nixcloud/TLS/foo.de/selfsigned/key.pem;
location /myleaps {
set $tttt http://127.0.0.1:3031/myleaps;
# https default flags
proxy_set_header Host $host;
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_pass $tttt;
}
location /myleaps/leaps/ws {
# https websocket default flags
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header X-Forwarded-For $remote_addr;
proxy_read_timeout 36000s;
# required because of CORS
proxy_set_header Host $host;
proxy_pass http://127.0.0.1:3031/myleaps/leaps/ws;
}
}
}
using hardcoded values (no set)
when using this:
proxy_pass http://127.0.0.1:3031/myleaps;
instead of:
proxy_pass $tttt;
it works for my example.
so the question is: why does proxy_pass work with an hard coded string but not with using a variable?
motivation
in the reverse-proxy here https://github.com/nixcloud/nixcloud-webservices/blob/be57d526547c66db05595002682525ca62c8f068/modules/services/reverse-proxy/default.nix#L144 the code which generates the proxy_pass is basically hardcoded and if a user wishes to override this it is going to be really complicated. so the idea is to generate 3 variables: ip, port and path and put it into the location scope and let the user later use these. this way i could get rid of the hardcoded proxy_pass directive and the user would be much more flexible in modifying/generating the location record.
so the code could be like this:
set $targetIP = ${location.ip};
set $targetPort = ${toString location.port}
set $targetPath = ${removeSuffix "/" (toString (builtins.toPath (location.path)))};
and the 3 lines above would always be generated into that location record, no matter what. but the code below is dynamically generated and a default implementation could be this:
proxy_pass http://$targetIP:$targetPort$targetPath;
but the user could simply override it by:
nixcloud.reverse-proxy = {
enable = true;
extendEtcHosts = true;
extraMappings = [
{
domain = "example.com";
path = "/";
https = {
mode = "on";
basicAuth."joachim" = "foo";
record = ''
proxy_set_header Host $host;
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_pass http://$targetIP:$targetPort$targetPath
'';
};
}
];
};
it is working using $request_uri instead of hardcoding the proxy_pass path using $targetPath:
set $targetIP 127.0.0.1;
set $targetPort 3031;
proxy_pass http://$targetIP:$targetPort$request_uri;
this code will soon be in nixcloud-webservices!
thanks to irc.freenode.net#nginx#benbrown (Ben Brown) for this solution!

nginx conditional proxy pass based on request body content

I am trying to configure nginx to proxy pass the request to another server, only if the $request_body variable matches on a specific regular expression.But it is not working for me.
server{
listen 80 default;
server_name www.applozic.com;
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $http_host;
if ($request_body ~* (.*)appId(.*)) {
proxy_pass http://apps.applozic.com;
}
}
}
request body is::
{
"applicationId": "appId",
"authenticationTypeId": 1,
"enableEncryption": false,
"notificationMode": 0,
"deviceType": 4,
}
I found the solution.
I did following changes in nginx(open resty) config file
upstream algoapp {
server 127.0.0.0.1:543;
}
upstream main {
server 127.0.0.1:443;
}
location /rest/ws/login {
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Url-Scheme $scheme;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_redirect off;
if ($request_method = OPTIONS ) {
proxy_pass https://127.0.0.1:543;
}
if ($request_method = POST ) {
set $upstream '';
access_by_lua '
ngx.req.read_body()
local data = ngx.req.get_body_data()
local match = ngx.re.match(ngx.var.request_body, "appId")
if match then
ngx.var.upstream = "algoapp"
else
ngx.var.upstream = "main"
end
';
proxy_pass https://$upstream;
}
}
As best I can tell the issue is that the variable $request_body may not have been read into memory at the time your if statement is executing.
Suggested alternatives would be to use the lua support or compile nginx with the echo modules and run echo_request_body.
That is not so easy when come to using integration , like some circumstances your nginx does not really allow you to do extra proxy stuff to change the whole design, then you can try nginx-if-request-body to achieve the result
http {
....
map "$uri" $forward_status {
default 100; # 100 means nothing return, continue to proxy phase
"~*.+?\.(css|js|bmp|gif|ico|jpeg|jpg|pict|png|svg|swf|tif)$" 418;
}
map "$request_body" $forward_status_by_body {
default 100;
"abc123xxx" 418;
"~*.+?\.(css|js|bmp|gif|ico|jpeg|jpg|pict|png|svg|swf|tif)$" 418;
}
server {
...
error_page 418 =200 #welcome_if_request_body;
location #welcome_if_request_body {
add_header Content-Type text/plain;
return 200 "welcome_if_request_body, you hit it";
}
location = / {
if_request_body on;
return_status_if_body_eq "ASD" 418 on;
return_status_if_body_eq "foo" 418;
return_status_if_body_eq "john" 418;
return_status_if_body_startswith "report" 418;
return_status_if_body_contains "report" 418;
return_status_if_body_regex "^[\d]+?abc" 418;
return_status_if_variable_map_to $forward_status;
return_status_if_variable_map_to $forward_status_by_body;
proxy_pass http://localhost:7777;
}
...
}
}

NGINX force SSL for all but health check file?

I have a Rails app with an NGINX reverse proxy behind an AWS ELB. I am terminating SSL on the ELB and I have NGINX configured to force any attempt at HTTP to rewrite as HTTPS. This setup works fine, but I'm also serving the site through ECS, and since the ELB health check is on HTTP port 80, when it gets the redirect and returns 301, the ELB health check fails and the instance is deregistered.
How do I set up NGINX to send all but the health check file through HTTPS?
Here is my server block from nginx.conf:
server {
listen 80;
server_name localhost;
root /var/www/html;
location ~ ^elbcheck\.html$ {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://rails_app;
break;
}
location / {
proxy_redirect off;
proxy_next_upstream error;
if ($http_x_forwarded_proto != "https") {
rewrite ^ https://$host$request_uri? permanent;
}
try_files $uri $uri/ #proxy;
}
location ~* \.(jpg|jpeg|svg|png|gif|ico|css|js|eot|woff|woff2|map)$ {
proxy_cache APP;
proxy_cache_valid 200 1d;
proxy_cache_valid 404 5m;
proxy_ignore_headers "Cache-Control";
expires 1d;
proxy_set_header Host $host;
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 https;
add_header X-Cache-Status $upstream_cache_status;
proxy_pass http://rails_app;
}
location #proxy {
proxy_set_header Host $host;
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 https;
proxy_pass http://rails_app;
}
}
I had the same problem, and found this answer somewhere on the internet (don't have the source anymore, it was a while ago)
server {
listen 80;
set $redirect_to_https 0;
if ($http_x_forwarded_proto != 'https') {
set $redirect_to_https 1;
}
if ($request_uri = '/status') {
set $redirect_to_https 0;
}
if ($redirect_to_https = 1) {
return 301 https://$host$request_uri;
}
...
}
Found a simple answer that worked great at this post. Here is what #ceejayoz suggested there:
server {
location /elb-status {
access_log off;
return 200;
}
}
Seems to be working--ECS hasn't terminated my service due to failed health checks anymore.

how to reverse proxy via nginx a specific url?

I have a server running at http://localhost:8080 i want a specific url of this server to be proxied by nginx.
For example, i only want http://localhost:8080/test/(.*) to be reverse proxied to http://localhost/test/(.*).
I'm proxing another server to http://localhost/.
What about just a simple location block?
server {
# ... other stuff
location /test/ {
try_files $uri #testproxy;
}
location #testproxy {
proxy_pass http://127.0.0.1:8080;
proxy_set_header X-Forwarded-Host $server_name;
proxy_set_header X-Real-IP $remote_addr;
# all your params
}
}
I made it somehow this way and it worked. Thanks for your comment anyway. :)
server {
listen 80;
# ... other stuff
upstream backend1 {
server 127.0.0.1:8080;
}
location /test/ {
proxy_pass_header Server;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
proxy_pass http://backend1/test/;
}
}

Resources