POST request body being lost with nginx reverse proxy - nginx

I'm using nginx as a reverse proxy for a http service using a configuration like this:
location /jobexecutor/ {
proxy_pass http://jobexecutor:8080/jobexecutor/;
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_redirect off;
proxy_connect_timeout 75s;
}
GET requests are being proxied to the service fine, but when I use POST the request is proxied to the service OK, but the body is empty. When POSTing to the service directly it works fine.
Any ideas what's wrong?

You have found a workaround, but I suspect not the root cause.
As per RFC7231 it's a known issue that 301 and 302 server responses often result in the conversion of request methods which are not safe to GET requests when following the redirect.
A normal proxy_pass should be transparent to the client, so it sounds like some other part of your Nginx configuration is doing some client redirection first, before the request gets proxied.
Once you determine where this is happening you can either reconfigure your Nginx conf to eliminate the redirect, or change the 301/302 response codes to 307/308 respectively, which redirect while maintaining the original request method.

I finally found the answer to this. The problem was with curl, in that when following a redirect it wants to convert the POST into a GET, but the -X arg seems to force it to keep it as a GET, but the body get's lost.
To get the expected behaviour you need to specify the --post301 or similar argument (as well as the -L argument).
See https://curl.haxx.se/docs/manpage.html#--post301

Related

Handling flask url_for behind nginx reverse proxy

I have a flask application using nginx for a reverse proxy/ssl termination, but I'm running into trouble when using url_for and redirect in flask.
nginx.conf entry:
location /flaskapp {
proxy_pass http://myapp:8080/;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
The idea is that a user navigates to
https://localhost:port/flaskapp/some/location/here
and that should be passed to flask as
http://localhost:8080/some/location/here
This works reasonably well when navigating to a defined route, however if the route has redirect(url_for('another_page')), the browser is directed to
http://localhost:8080/another_page
And fails, when the URL I actually want to go to is:
https://localhost:port/flaskapp/another_page
I have tried several other answers for similar situations, but none have seemed to be doing exactly what I am doing here. I have tried using _external=True, setting app.config['APPLICATION_ROOT'] = '/flaskapp' and many iterations of different proxy_set_header commands in nginx.conf with no luck.
As an added complication, my flask application is using flask-login and CSRF cookies. When I tried setting APPLICATION_ROOT the application stopped considering the CSRF cookie set by flask-login valid, which I assume has something to do with origins.
So my question is, how do I make it so that when flask is returning a redirect() to the client, nginx understands that the URL it is given needs flaskapp written into it?
I managed to fix it with some changes.
Change 1. Adding /flaskapp to the routes in my flask application. This eliminated the need for URL-rewriting and simplified things greatly.
Change 2. nginx.conf changes. I added logc in the location block to redirect http requests as https, new conf:
location /flaskapp {
proxy_pass http://myapp:8080/;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# New configs below
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
# Makes flask redirects use https, not http.
proxy_redirect http://$http_host/ https://$http_host/;
}
While I didn't "solve" the issue of introducing conditional rewrites based on a known prefix, since I only need one prefix for this app it is an acceptable solution to bake it into the routes.
In your situation I think the correct thing would be to use werkzeug's ProxyFix middleware, and have your nginx proxy set the appropriate required headers (specifically X-Forwarded-Prefix).
https://werkzeug.palletsprojects.com/en/0.15.x/middleware/proxy_fix/#module-werkzeug.middleware.proxy_fix
This should make url_for work as you would expect.
Edit: Snippet from #Michael P's answer
from werkzeug.middleware.proxy_fix import ProxyFix
app = Flask(__name__)
app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_host=1)

Restlet how to build relates HATEAOS links properly?

Building a webapp behind a reverse proxy/load balancer, I need to get the correct original URL of the request (pre load balancer rewrite).
I have used getReference() (in the ServerResource) to add a self reference in the HATEAOS sense. However the doc says that the getReference() can be manipulated by the routing, and currently it does not include the correct scheme (http, instead of https - the load balancer terminates the https).
Here are the NGINX configs with regards to the headers forwarded.
location /api {
proxy_pass http://test-service;
proxy_pass_header X-Host;
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;
}
Is the reverse proxy config incorrect, or should I use the getOriginalReference() method. Is there some documentation that explains how the "original" reference is constructed, which fields are used behind a revers proxy.
I think that the support of the X-Forwarded-For header must be explicitly enabled in Restlet due to potential security issues.
Here is the way to enable this feature as the server connector level:
Component c = new Component();
Server server = c.getServers().add(Protocol.HTTP, 8182);
server.getContext().getParameters().add("useForwardedForHeader", "true");
c.start();
See this page for more details: http://restlet.com/technical-resources/restlet-framework/guide/2.3/core/base/connectors.
Once done, the corresponding hints are available in the ClientInfo object:
List<String> forwardedAddresses
= request.getClientInfo().getForwardedAddresses();
See this page for the mapping between headers and Restlet API: http://restlet.com/technical-resources/restlet-framework/guide/2.2/core/http-headers-mapping.
Hope this helps you,
Thierry

Vaadin, Nginx. unsaved data

See image below of vaadin 7, nginx. What could be wrong?
web.xml
sample config:
server {
listen 80;
server_name crm.komrus.com;
root /home/deploy/apache-tomcat-7.0.57/webapps/komruscrm;
proxy_cache one;
location / {
proxy_set_header Host $host;
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_pass http://127.0.0.1:8080/komruscrm/;
}
}
As it seems (because you don't provide enough info about your problem) you are using nginx as reverse proxy for tomcat/jboss/jetty, and you are deploying a Vaadin application in it.
Just when you enter in the application, session expired message appears.
I had this problem 3 months ago. In my escenario Nginx was 1.0 and Vaadin 7.0+. The issue comes because of the cookies. I know that nginx must set or rewrite something in the cookies, but, you must set it manually in nginx.conf file, else, you will get that error.
Sadly, in my nginx version I wasn't able to pass cookies in the right way, so, I wasn't able to deploy my application under that scenario.
After some issues, I've decided to use Apache's reverse proxy, and never saw that issue again. Hope you can write a rule that enables to pass the cookies in the right way.
EDIT: I remembered this post How to rewrite the domain part of Set-Cookie in a nginx reverse proxy?, this is the case!

github oauth and nginx proxy

I am feeling that I have searched the complete internet and tried nearly everything to solve my problem. Now I decided to ask you and hope that there is anybody out there who is able to help me.
I have a node application running on sub2.domain.tld:3000. Now I want to proxy this application to port 80 with nginx in the way that I am able to reach the app with sub.domain.tld. But that is not the problem. I am able to reach the first site.
The problem follows by an authentification routine with OAuth-API to verify the user for the application.
When surfing to sub2.domain.tld:3000 the process works fine. But when I change the url in the configs and try to surf to sub.domain.tld the authentification process runs into an error (error=redirect_uri_mismatch&error_description=The+redirect_uri+MUST+match+the+registered+callback+URL.....).
So I guess I am making a mistake in the redirecting of the url with nginx.
I am using nginx 1.4.7 and node 0.10.26
My nginx configuration file looks like that:
server {
listen 80;
access_log /var/log/nginx/access_log_sub;
server_name sub.domain.tld;
location / {
include proxy_params;
proxy_pass http://IP:3000;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Client-IP $remote_addr;
proxy_set_header X-Forwarded-for $remote_addr;
}
}
But I belive OAuth is verifying sub2.domain.tld:3000 and that it gets in conflict with sub.domain.tld
I hope you are able to help me, solving this issue.
The error isn't coming from nginx, it's coming from your OAuth provider:
The redirect_uri parameter is optional. If left out, GitHub will redirect users to the callback URL configured in the OAuth Application settings. If provided, the redirect URL's host and port must exactly match the callback URL. The redirect URL's path must reference a subdirectory of the callback URL.
-- https://developer.github.com/v3/oauth/#redirect-urls
This is an old question, but...
Try changing your Host header to
proxy_set_header Host $host:$server_port
This may or may not work depending on your application.
As an aside, X-Forwarded-For should include a comma-separated list of the originating client and any proxies it passes through.

How should you forward the value of HTTP port when proxying?

So, I have an nginx doing reverse proxying to a rails server. The rails server has an oauth login, and the lib that does it builds the callback URL using 'X-Forwarded-Host'. The issue is that when nginx is listening on a port other than 80 the callback URL is not being properly formatted. Looking at the configuration I realized this is because it builds the URL from 'X-Forwarded-Host', and the config I used did not include the port in it. I have modified my configuration to the following to make this work:
server {
listen 8081;
server_name app;
location / {
proxy_pass http://app;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Host $host:8081;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_redirect off;
}
}
My question is, what is 'X-Forwarded-Host' actually defined as? Nginx treats 'Http-Host' as the host + port, but I've found around the net that sometimes X-Forwarded-Host is treated as the host only, and there seems to be a variable called 'X-Forwarded-Port' that is sometimes used but I couldn't find anything in the nginx docs about it except that there is a variable available to print in the logs called 'proxy-port', but this is the port being forwarded to, rather than the port it accepted the connection on (which for me is nothing, because I'm using a unix socket). What's the proper solution? Nginx does not allow me to a X-Forwarded-Port header manually, and I'm not even sure that I should. Looking around the net, it appears that other http servers treat this variably differently, for example:
https://github.com/nodejitsu/node-http-proxy/issues/341
http://mattrobenolt.com/handle-x-forwarded-port-header-in-django/
Some related links:
Someone asserts the definition of Http-Host:
http://ask.wireshark.org/questions/22988/http-host-header-with-and-without-port-number
Someone saying there's no standards for these headers:
What is a full specification of X-Forwarded-Proto HTTP header?
An unanswered, related stack overflow:
https://serverfault.com/questions/536576/nginx-how-do-i-forward-a-http-request-to-another-port
My question is, what is 'X-Forwarded-Host' actually defined as?
It's not defined, which is why things are so inconsistent. I've seen the port specified separately and as part of the host. For what it's worth, specifying it with the host seems common.
I just had a similar question. I think a common way is to specify the port in X-Forwarded-Host header as Daniel Fowler suggested in his answer.
I also saw that sometimes "unofficial" X-Forwarded-Port header is used.
I created a similar question where I summarize how I think the servers should behave - all possible combinations (also with X-Forwarded-Proto which I think can also have impact).
The server "app" has nginx on port 8081 and rails on 80.
This maps all requests to http://app:80, and changes the response-header "Location" from http://app:80 to http://app:8081.
server {
listen 8081;
server_name app;
location / {
proxy_pass http://app:80;
proxy_redirect http://app/ http://app:8081/;
proxy_redirect http://app:80/ http://app:8081/;
}
}

Resources