Blauspecht
Blauspecht

Reputation: 113

How to reroute POST/PUT/... in Nginx to internal service?

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

Upvotes: 3

Views: 1286

Answers (2)

ti7
ti7

Reputation: 18792

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;
}

Upvotes: 2

ampularius
ampularius

Reputation: 389

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

Upvotes: 2

Related Questions