3

I have a server (ubuntu 20.04 core) with nginx installed. On the same server I have a running Spring Boot Web(2.3.3.RELEASE) service on port 8080.

I want to access the resources from the Spring Boot Web service externally with e.g. http://server/api/users to access http://localhost/api/users and return that response to the client.

I have the following rule configured in nginx:

location /api {
    proxy_pass http://localhost:8080;
}

This works fine for GET request, but doesn't for POST or PUT. The first response to the client is a response with status code 301: Moved permanently and according to HTTP spec a client should change the HTTP method to either GET or HEAD. The client changes the method automatically after the first response as seen in the nginx logfile at /var/log/nginx/access.log:

192.168.0.1 - - [14/Oct/2020:11:23:56 +0100] "POST /api/users?key=key HTTP/1.1" 301 162 "-" "PostmanRuntime/7.26.5"
192.168.0.1 - - [14/Oct/2020:11:23:56 +0100] "GET /api/users?key=key HTTP/1.1" 200 93 "http://server/api/users?key=key" "PostmanRuntime/7.26.5"
192.168.0.1 - - [14/Oct/2020:11:23:59 +0100] "PUT /api/users/1/deleted?key=key&deleted=false HTTP/1.1" 301 162 "-" "PostmanRuntime/7.26.5"
192.168.0.1 - - [14/Oct/2020:11:23:59 +0100] "GET /api/users/1/deleted?key=key&deleted=false HTTP/1.1" 405 141 "http://server/api/users/1/deleted?key=key&deleted=false" "PostmanRuntime/7.26.5"

The GET-Request from the PUT-Request failed with 405 because theres no mapped GET for path "/api/users/{id}/deleted". I tried adding and modifing many different configurations in the "location /api {..}" for example:

location /api {
  proxy_pass      http://localhost:8080;
  proxy_redirect  http://localhost:8080/ /; # I tried "../api /", ".../api/ /", ".../ /api" 
  proxy_read_timeout 60s;

  proxy_set_header          Host            $host;
  proxy_set_header          X-Real-IP       $remote_addr;
  proxy_set_header          X-Forwarded-For $proxy_add_x_forwarded_for;
}

Suggested in serverfault(how do I get nginx to forward HTTP POST requests via rewrite?)

I found the exact same question on trac.nginx.com but that config didn't work either:

location /api { # "/api" and "/api/" doesn't work
    proxy_pass ​http://localhost:8080;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
}

I am currently quite lost with what else I should try to get it to work.

--- EDIT: With the help of @ti7 and @ampularius the working solution now is:

NGINX location entry:

location /api {
    proxy_pass http://localhost:8080;
    proxy_redirect off;
    proxy_set_header X-Forwarded-Host $host;
    proxy_set_header X-Forwarded-Server $host;
    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;
}

And in application.properties of the Spring Boot Service no changes were made, so NONE of these settings does anything for this case:

server.use-forward-headers=true
server.forward-headers-strategy=native
server.forward-headers-strategy=framework
server.forward-headers-strategy=none
Community
  • 1
  • 1
Blauspecht
  • 113
  • 1
  • 5

2 Answers2

2

This likely happens because your backend server thinks the request is http and redirects it to https. Try adding this in your location block:

proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Port  $server_port;

It lets your backend server know that the request is in fact coming with https but tls is terminated further up (in most cases you have to whitelist your proxyserver to make it work).

According to this answer you can do that like this in application.properties:

server.use-forward-headers=true
ampularius
  • 367
  • 1
  • 8
  • Sadly thats not, at least I think so: I added "server.forward-headers-strategy=NATIVE" since "server.use-forward-headers" is deprecated and strategy=NATIVE should do the same according to [this answer](https://stackoverflow.com/a/59126519/9358945). – Blauspecht Oct 14 '20 at 14:10
  • @Blauspecht In this case you should be able to just add the X-Forwarded-Proto and X-Forwarded-Port. At least I think so (Assuming you connect to the proxy with https). – ampularius Oct 14 '20 at 14:17
  • Update: I tried it with "server.forward-headers-strategy=framework" and your suggestion "server.use-forward-headers=true" aswell, didn't change a thing. You mentioned https, I have https enabled in nginx and a reroute from http to https, does this have anything to do with it? – Blauspecht Oct 14 '20 at 14:23
  • but did you add the two proxy_set_header lines I suggested to the nginx config? They are important. – ampularius Oct 14 '20 at 14:25
  • That was the first thing I did, yes. Now theres proxy_pass and the two proxy_set_header lines in the location /api {...}. Logically I restartet nginx too. – Blauspecht Oct 14 '20 at 14:28
  • You have to keep the proxy_set_header you already had and just add the proto and port ones, so five times proxy_set_header in total. If this doesn't help I don't know what the issue is. – ampularius Oct 14 '20 at 14:31
  • I now added all five proxy_set_header (-Host, -Server, -For, -Proto and -Port), tried with application.properties: "server.use-forward-headers=true", "server.forward-headers-strategy=native", "server.forward-headers-strategy=framework" or "server.forward-headers-strategy=none" and it did nothing, thats sadly not the answer but thanks anyways for trying! – Blauspecht Oct 15 '20 at 05:56
2

Recently I've been working a good deal with the excellent Gunicorn WSGI server with an nginx frontend.

The deploy docs suggest disabling proxy_redirect with proxy_pass and the Host header set, which could be the source of your troubles!

This has been working great to handle POST and GET requests, albeit with a wildly-different webserver.

location @proxy_to_app {
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header Host $http_host;
    # we don't want nginx trying to do something clever with
    # redirects, we set the Host: header above already.
    proxy_redirect off;
    proxy_pass http://app_server;
}
ti7
  • 16,375
  • 6
  • 40
  • 68
  • So your suggestion is to use two webservers on one machine? – Blauspecht Oct 17 '20 at 17:29
  • @Blauspecht sorry, no - I am suggesting you disable `proxy_redirect`! – ti7 Oct 18 '20 at 05:30
  • 1
    Wow, that and the settings of ampularius worked. Thanks a lot guys! So now the location entry looks like this `location /api { proxy_pass http://localhost:8080; proxy_redirect off; proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-Server $host; 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; } ` – Blauspecht Oct 20 '20 at 12:26
  • @Blauspecht Excellent! it's probably most appropriate to either 1) create a new Answer which presents your final code or 2) edit it into a new code block in your Question (`---` **EDIT These two parts became my final code:** ) to make it really clear for later readers! – ti7 Oct 20 '20 at 16:17
  • I'm afraid **Lukas Rotter** who placed the bounty cannot split it. Rather than making them choose, I would be delighted to accept it and re-award half to **ampularius** or coordinate the reverse, as it's quite significant! – ti7 Oct 20 '20 at 17:12
  • 1
    Yeah, I thought the same, I wanted to split it but thats wasn't possible.. Thank you for giving @ampularius his share! – Blauspecht Oct 23 '20 at 05:26