Route different base path to same proxy pass Nginx - nginx

I want to pass different path to the same proxy_pass but I keep getting 502 Bad gateway.
These path use the same port number but different base path. How do I make it work from what I have which returns an error currently.
this is what my current location looks like
worker_processes 4;
# worker_process auto
events { worker_connections 1024; }
http {
server {
listen 80;
charset utf-8;
location ~ ^/api/v1/(wallet|card)/(.*)$ {
proxy_pass http://wallet-service:3007/api/v1/$1/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'Upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
}

If you don't need an URI to be changed at all, don't use anything other than the upstream name:
location ~ ^/api/v1/(wallet|card)/ {
proxy_pass http://wallet-service:3007;
...
}
If an URI needs to be rewritten before being passed to the upstream, check this answer to see how to do it.

Related

nginx: Match multiple locations / disable access log without returning

I would like to disable access logging for some specific paths but still proxy it to another container. In other words "match multiple locations without returning/exiting" which is not possible as far as I know.
The following config will make nginx cancel the request without entering the proxy pass location.
server {
# ...
# do not log requests for /_nuxt/* and /_ipx/*
location ~ ^/(_ipx|_nuxt) {
access_log off;
}
# still proxy these paths
location ~* ^(\/|\/(foo|bar|_nuxt|_ipx)$ {
proxy_pass http://frontend:3000;
proxy_http_version 1.1;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $server_name:$server_port;
}
}
Is there a more clean way of achieving the desired behavior other than duplicating the proxy configuration and adding the access log config line to that second location?
server {
# ...
# proxy pass without _nuxt and _ipx
location ~* ^(\/|\/(foo|bar)$ {
proxy_pass http://frontend:3000;
proxy_http_version 1.1;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $server_name:$server_port;
}
# access log + proxy pass
location ~ ^/(_ipx|_nuxt) {
access_log off;
proxy_pass http://frontend:3000;
proxy_http_version 1.1;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $server_name:$server_port;
}
}
You're right, location working like switch case taking the first hit and break.
Maybe you can try something like that:
if ($request_uri ~ ^/(_ipx|_nuxt)) {
access_log off;
}
instead of your first location statement.
The access_log directive has the following syntax:
access_log path [format [buffer=size] [gzip[=level]] [flush=time] [if=condition]]; ...
...
The if parameter (1.7.0) enables conditional logging. A request will not be logged if the condition evaluates to “0” or an empty string. In the following example, the requests with response codes 2xx and 3xx will not be logged:
map $status $loggable {
~^[23] 0;
default 1;
}
access_log /path/to/access.log combined if=$loggable;
Applied to the asked question, that means the following config should achieve the desired goal:
map $uri $loggable {
~^/_(ips|nuxt) 0;
default 1;
}
server {
...
access_log /path/to/access.log <format> if=$loggable;
}

www in domain return timeout, after I add redirect using regex in nginx

I have nginx and I want to redirect non-www to www:
So I did it using regex, but the redirect seems to works I get the redirect, but www return timeout.
How can I solve this?
upstream wwwapp {
least_conn;
server www-app:3000 weight=10 max_fails=3 fail_timeout=30s;
}
server {
server_name ~^(?!www\.)(?<domain>.+)$;
return 301 $scheme://www.$domain$request_uri;
}
server {
listen 80;
location / {
proxy_pass http://wwwapp;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
Easy.
server {
listen 80;
if ($host ~ ^(?!www\.)(?<domain>.+)$) {
return 301 $scheme://www.$domain$request_uri;
}
location / {
proxy_pass http://wwwapp;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
Your second server block does not have a server_name so it is unlikely to get selected to process any requests.
The first server block is the default and will get selected even if the regex does not match.
You should change the second server block, and either add a server_name statement or make it the default_server.
See this document for details.

Let Nginx pass current location

I have nginx reverse-proxy to my site on IIS and here is my nginx config:
UPDATE
upstream backend {
server 43.128.77.101;
}
server {
server_name domain.subdomain.com;
location /products {
if ($query_string ~ Jeans){
return 301 /get-all-products/?filter=jeans;
}
if ($query_string ~ Shirts){
return 301 /get-all-products/?filter=shirts;
}
if ($query_string ~ Hats){
return 301 /get-all-products/?filter=hats;
}
}
location / {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
It redirects from /products page to certain URLs by query string. But for page /products-available it fails with error 404. Nginx error log contains error:
"/usr/share/nginx/html/products-available" failed (2: No such file or directory)
The page /products-available doesn't need any redirections. I want it to pass on backend IIS server as it is. How can I tell nginx to pass it through? What am I doing wrong?
Thank you.
This would be because you are only defining the behavior of Nginx for a given path (/products).
If you want to define a default behavior for Nginx requests that don't match the /products path (like /products-available) you can add the following after your current location section to proxy any other path request to a different application/port.
location / {
proxy_pass http://127.0.0.1:3000;
}
You can see more information on sending a request to a different application in https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/#passing-a-request-to-a-proxied-server

Nginx proxy_pass returns [No Host] while reverse proxying

I'm trying to reverse proxy an api with Nginx. I have the following configuration:
worker_processes 4;
events { worker_connections 1024; }
http {
upstream some_upstream {
server 1.something.com;
server 2.something.com;
}
server {
listen 80;
location ~/proxyNow/(?<zvar>(\w+))/(?<xvar>(\w+))/(?<yvar>(\w+))/ {
proxy_pass http://some_upstream/hello/something/$zvar/$xvar/$yvar/somethingelese;
proxy_set_header Host $http_host;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_cache maps_cache;
proxy_cache_valid 200 302 365d;
proxy_cache_valid 404 1m;
proxy_redirect off;
}
}
}
When I try to call the following url http://localhost:82/proxyNow/1/2/3/?app_id=someAppId&app_code=someCode
I get the following error message:
Invalid URL
The requested URL
"http://%5bNo%20Host%5d/hello/something/1/2/3/somethingelese", is
invalid. Reference #9.be35dd58.1489086561.5c9bd3c
It seems that the host cannot be retrieved by nginx. But if I execute the call directly:
http://1.something.com/hello/something/$zvar/$xvar/$yvar/somethingelese?app_id=someAppId&app_code=someCode
http://2.something.com/hello/something/$zvar/$xvar/$yvar/somethingelese?app_id=someAppId&app_code=someCode
It seems that Nginx for some reason is not able to resolve the host
You should take a look into the doc.
http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_pass
When location is specified using a regular expression.
In this case, the directive should be specified without a URI.
I suggest the following solution without expensive regex location.
http://nginx.org/en/docs/http/ngx_http_core_module.html#location
location /proxyNow/ {
rewrite /proxyNow/(?<zvar>(\w+))/(?<xvar>(\w+))/(?<yvar>(\w+))/.* /hello/something/$zvar/$xvar/$yvar/somethingelese$is_args?$args break;
proxy_pass http://some_upstream;
... other nginx diretives;
}

Nginx Reverse Proxy Websocket Authentication - HTTP 403

I'm using Nginx as a reverse proxy of a Spring boot application. I also use Websockets with sockjs and stomp messages.
Here is the context configuration.
<websocket:message-broker application-destination-prefix="/app">
<websocket:stomp-endpoint path="/localization" >
<websocket:sockjs/>
</websocket:stomp-endpoint>
<websocket:simple-broker prefix="/topic" />
</websocket:message-broker>
Here is the client code:
var socket = new SockJS(entryPointUrl);
var stompClient = Stomp.over(socket);
var _this = this;
stompClient.connect({}, function () {
stompClient.subscribe('/app/some-url', function (message) {
// do some stuff
});
});
I also you Spring Security to protect some content.
#Configuration
#Order(4)
public static class FrontendSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/js/**", "/css/**", "/webjars/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin().loginPage("/login").permitAll()
.and()
.logout().permitAll();
}
}
Everything works great, expect when I run this application behind a Nginx reverse proxy. Here is the reverse configuration:
proxy_pass http://testsysten:8080;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# WebSocket support (nginx 1.4)
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $http_connection;
# Max body size
client_max_body_size 10M;
The connection always fails with a HTTP 403 code.
I'm using version 1.9.7.
Do you have any idea, why the client does not gets authenticated?
I know similar questions, like this one but the solutions do not work at all.
Update
I managed to run the application over HTTP. I need to pass the CSRF token in the Nginx configuration. New configuration is:
proxy_pass http://testsysten:8080;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# Pass the csrf token (see https://de.wikipedia.org/wiki/Cross-Site-Request-Forgery)
# Default in Spring Boot
proxy_pass_header X-XSRF-TOKEN;
# WebSocket support (nginx 1.4)
proxy_http_version 1.1;
Only missing is redirect over HTTPS. In the Spring logs is see following entry:
o.s.w.s.s.t.h.DefaultSockJsService - Processing transport request: GET http://testsystem:80/localization/226/3mbmu212/websocket
Seems like Nginx Proxy needs to rewrite the to the right port.
I solved the problem by myself. Basically, Nginx needs to pass some additional header values if you want to use Websocket and Spring Security. The following lines need to be added to location section in your Nginx config:
# Pass the csrf token (see https://de.wikipedia.org/wiki/Cross-Site-Request-Forgery)
# Default in Spring Boot and required. Without it nginx suppresses the value
proxy_pass_header X-XSRF-TOKEN;
# Set origin to the real instance, otherwise a of Spring security check will fail
# Same value as defined in proxy_pass
proxy_set_header Origin "http://testsysten:8080";
The accepted solution did not work for me although I was using a very classical HTTPS configuration:
server {
listen 443 ssl;
location /ws {
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $http_host;
proxy_pass http://127.0.0.1:8888;
}
...
The problem is that Spring checks the origin and specifically that code was causing me trouble:
// in org.springframework.web.util.UriComponentsBuilder.adaptFromForwardedHeaders(HttpHeaders):
if ((this.scheme.equals("http") && "80".equals(this.port)) ||
(this.scheme.equals("https") && "443".equals(this.port))) {
this.port = null;
}
In that code the scheme is 'http' and the port is 8888, which is not discarded because it is not the standard port.
The browser however hits https://myserver/ and the 443 port is omitted because it is the default HTTPS one.
Therefore the ports do not match (empty != 8888) and origin check fails.
Either you can disable origin checks in Spring WebSockets:
registry.addHandler( resgisterHandler(), "/ws" ).setAllowedOrigins( "*" );
or (probably safer) you can add the scheme and port to the NGINX proxy configuration:
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Port $server_port;
If you are interested, those headers are read in
org.springframework.web.util.UriComponentsBuilder.adaptFromForwardedHeaders(HttpHeaders)
For Spring Boot 2.2.2+
Starting with Spring Boot version 2.2.2 you should be adding following setting for these X-Forwarded-* headers to be taken into account:
server.forward-headers-strategy=native
(in application.properties for instance)
I had faced a similar problem. I was unable to use the basic Spring Security authentication with NGINX. Apart from setting the proxy_pass_header X-XSRF-TOKEN;, I also had to set underscores_in_headers on;, since NGINX by default does not allow headers with underscores and the CSRF token is named _csrf.
So my final configuration file looked like this:
server {
underscores_in_headers on;
listen 80 default_server;
listen [::]:80 default_server ipv6only=on;
root /usr/share/nginx/html;
index index.html index.htm;
# Make site accessible from http://localhost/
server_name localhost;
location / {
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
try_files $uri $uri/ =404;
# Uncomment to enable naxsi on this location
# include /etc/nginx/naxsi.rules
}
location /example/ {
proxy_pass_header X-XSRF-TOKEN;
proxy_pass http://localhost:8080/;
}
}
I solved this problem without CSRF header in NGINX proxy.
My stack: spring-boot, spring-security (with redis session store), spring-boot-websocket with default STOMP implementation, NGINX to serve frontend and proxied to another services that frontend consume.
In first time I use the default configuration show in the NGINX Blog here and here (copy and paste for history):
http {
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
upstream websocket {
server 192.168.100.10:8010;
}
server {
listen 8020;
location / {
proxy_pass http://websocket;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
}
}
But dont work, still 403 Forbidden.
I fixed this issue with the configuration below (the real important part to fix websocket is # WebSocket Proxy):
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 30010;
server_name localhost;
client_max_body_size 10M;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
# Backend API Proxy
location /api {
proxy_pass http://192.168.0.100:30080;
proxy_set_header Host $http_host;
proxy_set_header Access-Control-Allow-Origin 192.168.0.100;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-NginX-Proxy true;
rewrite ^/api/?(.*) /$1 break;
proxy_redirect off;
}
# CDN Proxy
location ~ ^/cdn/(.*) {
proxy_pass http://192.168.0.110:9000;
rewrite ^/cdn/(.*) /$1 break;
}
# This is the configuration that fix the problem with WebSocket
# WebSocket Proxy
location /ws {
proxy_pass http://192.168.0.120:30090;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $http_host;
proxy_set_header Access-Control-Allow-Origin 192.168.0.120;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-NginX-Proxy true;
}
}
}
In my case (Spring Boot app), in addition to setting the Origin header as specified in the accepted answer, I had to set the Host header to match the ip:port of the Origin header, or to get rid of it altogether.
This is my working vhost config:
server {
listen 443 ssl;
listen [::]:443 ssl;
ssl_certificate /etc/ssl/certs/<your-cert-file>.pem;
ssl_certificate_key /etc/ssl/private/<your-key-file>.key;
server_name <your-server-fqdn>;
access_log /var/log/nginx/<your-server-fqdn>.access.log;
error_log /var/log/nginx/<your-server-fqdn>.error.log error;
root /srv/www/<your-server-fqdn>;
index index.html index.html;
location / {
try_files $uri $uri/ /index.html;
}
location /api {
proxy_pass http://127.0.0.1:8080/v1;
}
location /async-api {
proxy_pass http://127.0.0.1:8080/stomp;
proxy_http_version 1.1;
# either set Host header as follows or get rid of the directive altogether
#proxy_set_header Host "127.0.0.1:8080";
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
# Set origin to the real instance, otherwise a of Spring security check will fail
# Same value as defined in proxy_pass
proxy_set_header Origin "http://127.0.0.1:8080";
proxy_set_header X-Forwarded-Host $host:$server_port;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /admin-api {
proxy_pass http://127.0.0.1:8080/api;
}
}

Resources