How do I rewrite request_uri variable in a location match - nginx

Java backend give me several interfaces like:
/admin/login
/client/query
/agent/update
I am a frontend engineer and I have to use these interfaces with ajax. Because we will deploy our projects on the same server, so I need to use a nginx proxy to direct those ajax request to the java service. But as you can see the interfaces start with different paths, so I prefixed them with "/api" in my code, and also configure nginx as below
axios.post('/api/admin/login'),
axios.post('/api/client/query'),
axios.post('/api/agent/update')
location /api {
proxy_pass http://127.0.0.1:8080$request_uri;
proxy_set_header Host 127.0.0.1;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
here my problem come:
Actually the java service get the request path: /api/admin/login, not /admin/login, so they can not handle that request. Can I rewrite my request_uri in nginx so that java get requests without /api prefix?

You don't need any rewrites for this. Use following location block:
location /api/ {
proxy_pass http://127.0.0.1:8080/;
proxy_set_header Host 127.0.0.1;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
See more info about slashes in proxy_pass directive here.

Copy pasted and adapted from a different question but should do the trick.
location /api {
rewrite ^/api(/.*)$ $1 last;
}

Related

Nginx Location Regex for keycloak

I have a keycloak, react web and a spring boot app are living behind nginx.
The frontend (react) and the backend (spring boot) are calling the keycloak service over the nginx proxy at the flowing urls:
realms/.../..etc
admin/.../..etc
js/.../.../..etc
resources/.../...etc
All those urls are reverse proxing to:
Http://keylock:8180
I don't want to implements multiple locations in nginx for all those urls!
I am thinking about url rewriting to
auth/realms/...
auth/dmin/..
...
Or another clean solution with regex, but i don't know how.
You can use the rewrite module for this.
location /auth {
rewrite ^/auth(/|$)(.*) /$2/ break;
proxy_pass http://keylock:8180;
}
In fact, with this method, I get the second part of the url and proxy it.
For example, when request send to /auth/realms/... the url rewrite to /realms/... and send it to keycloak http://keylock:8180/realms/...
this worked for me
location ~ ^/(realms|js|resources|admin)/ {
proxy_set_header Referer $http_referer;
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_set_header X-Forwarded-Port $server_port;
proxy_set_header Host $http_host;
proxy_pass http://keycloak;
proxy_redirect off;
}

Nginx location to FLASK route

I have 2 docker containers running. 1 is FLASK and 2 is Nginx.
This is the configuration of the Nginx location
location /search/?(.*) {
proxy_pass http://backend:8080/search/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
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_request_headers on;
}
The FLASK is running on port 8080 and the route #app.route('/search', methods=['GET']) and running with app.config['SERVER_NAME'] = 'mydomain.com'
When I try to send a GET request to "mydomain.com" (going through the NGINX) from external I get
Cannot GET /search
When I remove app.config['SERVER_NAME'] = 'mydomain.com' from the FLASK I can send GET to EXTERNAL_IP:8080 (Directly to the backend not though the NGINX).
But when I leave the app.config['SERVER_NAME'] = 'mydomain.com' and try to GET mydomain.com:8080 I get 404 Not Found
What am I missing here?
I want all requests to go though the NGINX and I want to use mydomain.com
I think your main mistake is that you trying to match the whole request URI including the query part while location directive (as well as the rewrite one) works with the normalized URI which doesn't include the query part at all (check the location directive documentation to find out what URI normalization is). Looks like you are also trying to use a regex while regex location should be declared using a ~ (or ~*) modifier. Nevertheless you don't need any regex locations at all for your particular case. To proxy a single API endpoint preservig the Host HTTP header value, you can try this one:
location = /search {
proxy_pass http://backend:8080;
proxy_set_header Host $http_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;
}
You don't need to specify an URI with the proxy_pass directive since you are not changing your request URI. If your API endpoint is /search/ rather than /search, change the location accordingly. If it is an URI path prefix rather than a single API endpoint, use a prefix location /search/ { ... } instead of exact match location.

Nginx ignores access_by_lua_file with proxy_pass

I use OpenResty in my project and faced issue that nginx ignores access_by_lua_file when use proxy pass. Here is my code of location:
location /getapi {
internal;
set $apiauth '';
set $api_host '';
access_by_lua_file /usr/local/openresty/nginx/conf/lua/getapi.lua;
proxy_redirect default;
proxy_pass $api_host;
proxy_ssl_certificate "/usr/local/openresty/nginx/conf/cert.pem"
certificate_key "cert.key";
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Authorization $apiauth;
}
I call this location with ngx.location.capture. In lua file i define variables apiauth and api_host. But contents of lua file never executes, nginx just ignores it. And no errors in error.log. The only one is that i try to GET empty URL. How can i force nginx to execute contents of access_by_lua_file?
Thanks to #IvanShatsky. Rewrite_by_lua works fo me.

Nginx - proxy_pass to google storage bucket does not use the index directive

I am trying to set my root(/) location to be passed to a google bucket.
Here is my configuration:
listen 80;
location / {
rewrite /(.*) /$1 break;
proxy_pass https://storage.googleapis.com/my-google-bucket-name/$1$is_args$args;
proxy_redirect off;
index my_main.html;
proxy_set_header Host "storage.googleapis.com";
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_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
This seems to work but the index directive does not set the default page (to my_main.html)
e.g. when i go to http://my_enginx_url/ instead of reaching "my_main.html" i reach the google bucket root page that shows an XML file with all the files in that bucket.
P.S
Both
http://my_enginx_url/another_page.html,
http://my_enginx_url/yet_another_page.html
are working fine.
Any ideas?
Because the index directive tells Nginx the names of files to look for within your file system which are appropriate for serving a request ending with a /
You are proxying the request to another server, so it's not applicable here.
To achieve your desired result create another location directive above your current one and use = to tell Nginx this is to handle only requests for an exact match with http://my_enginx_url/
Something like this:
location = / {
proxy_pass https://storage.googleapis.com/my-google-bucket-name/my_main.html;
......
}

Avoid duplication in password protect URL with proxy in nginx

I have a Flask application served using gunicorn, and with NGINX on top of it. I want to use Basic Authentication (user/password) to protect all URL's starting with /admin, which is the back office, but still continue serving all other URLs with gunicorn without password.
Here is my current NGINX config:
server {
listen 80;
server_name example.com;
charset utf-8;
location / {
proxy_pass http://localhost:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /admin {
auth_basic "Administrator Login";
auth_basic_user_file /home/app/.htpasswd;
# the following four directives are duplicated :(
proxy_pass http://localhost:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
If I don't duplicate the proxy_* directives in the second location block, then the URLs starting with /admin doesn't get forwarded to gunicorn and I get a 404.
Is there any way to avoid the configuration duplication? I tried location nesting but apparently in the end NGINX only "executes" a single location block.
The proxy_pass must be within the location block. However, there's no need to duplicate the proxy_set_header directives, they can be moved into the server block. So your mistake was simply the assumption that proxy_pass could live in the server block :-)

Resources