Nginx unknown directive "if($http_user_agent" - nginx

I try to return 503 status code when the user agent header has a specific value. I tried outside and inside the location block. But when I reload the this config nginx failes to reload:
upstream api{
server 127.0.0.1:1336;
}
# the nginx server instance
server {
listen 0.0.0.0:80;
server_name api.project.com;
# if($http_user_agent = "android") {
# return 503;
# }
# pass the request to the node.js server with the correct headers
location / {
# if($http_user_agent = "android") {
# return 503;
# }
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_set_header X-NginX-Proxy true;
proxy_pass http://api/;
proxy_redirect off;
}
}
Any ideas whats wrong? I am using nginx/1.4.7
From syslog:
nginx: [emerg] unknown directive "if($http_user_agent"

Add a space between if and (. that should do the trick!

Related

Regex in nginx inside map

I cannot fix routing in nginx for different parts in URI . So if the request has URI starting with de it should pass the traffic to app_b. That does not happen and I'm getting error: invalid URL prefix in "http://"
Here's the config.
map $request_uri $resources_location {
"/" "app_a:1234/";
"^.*de.*$" "app_b:2345/";
}
server {
listen 80;
listen [::]:80;
server_name example.com;
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header HOST $http_host;
proxy_set_header X-NginX-Proxy true;
proxy_pass http://$resources_location;
proxy_redirect off;
}
location ~ ^/(assets|public|favicon.ico) {
proxy_pass http://$resources_location;
}
}
How that can be solved (also with a help of map)?

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 proxy requests for a specific path

Is it possible to pass requests for a specific path to a different upstream server?
Here is my nginx site configuration:
upstream example.org {
server 127.0.0.1:8070;
keepalive 8;
}
server {
listen 0.0.0.0:80;
server_name example.org www.example.org;
access_log /var/log/nginx/example.org.log;
location / {
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_set_header X-NginX-Proxy true;
proxy_pass http://example.org;
proxy_redirect off;
}
}
Currently, requests to this site are redirected to a Node.js instance running on port 8070.
I would like requests to this site that have a path starting with /services to be redirected to another Node.js instance running on port 8080.
Is this possible? And of course -- how so?
Yes, just add another location block:
upstream example.org {
server 127.0.0.1:8070;
keepalive 8;
}
upstream other.example.org {
server 127.0.0.1:8080;
keepalive 8;
}
server {
listen 0.0.0.0:80;
server_name example.org www.example.org;
access_log /var/log/nginx/example.org.log;
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_set_header X-NginX-Proxy true;
proxy_redirect off;
location / {
proxy_pass http://example.org;
}
location /services {
proxy_pass http://other.example.org;
}
}
Note: I extracted all shared proxy directives into the server block so that they are not repeated in each location block. If they would differ between different locations, you would have to move them again into the location blocks...

nginx redirect all http to https with exceptions

I would like to redirect all http traffic to https with a handful of exceptions. Anything with /exception/ in the url I would like to keep on http.
Have tried the following suggested by Redirect all http to https in nginx, except one file
but it's not working. The /exception/ urls will be passed from nginx to apache for some php processing in a laravel framework but that shouldn't matter.
Any suggestions for improvement much appreciated!
server {
listen 127.0.0.1:80;
location / {
proxy_pass http://127.0.0.1:7080;
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-Accel-Internal /internal-nginx-static-location;
access_log off;
}
location /exception/ {
# empty block do nothing
# I've also tried adding "break;" here
}
return 301 https://localhost$request_uri;
}
Nginx finds the longest matching location and processes it first, but your return at the end of the server block was being processed regardless. This will redirect everything but /exception/ which is passed upstream.
server {
listen 127.0.0.1:80;
access_log off;
location / {
return 301 https://localhost$request_uri;
}
location /exception/ {
proxy_pass http://127.0.0.1:7080;
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-Accel-Internal /internal-nginx-static-location;
}
}

Resources