2

I have an application that is not SSL aware behind nginx, thus I need to do the following

http://example.com/f1/f2/page?next_page=http%3A//example.com/f3/new_page

has to change to

https://example.com/f1/f2/page?next_page=https%3A//example.com/f3/new_page

So there's two things to do, change the scheme, which I was able to, and change the url param, which I've been somewhat successfull at, but it doesn't work completely.

I found a page that did what I want to do, but it doesn't work for me: https://blog.imaginea.com/modifying-query-parameters-nginx-in-reverse-proxy-mode/

relevant part of my nginx config:

server {
    listen 443 ssl;
    server_name example.com;

    ssl on;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_certificate /path/to/bundle.crt;
    ssl_certificate_key /path/to/bundle.key;
    ssl_ciphers HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4;
    ssl_prefer_server_ciphers on;

    underscores_in_headers on;
    rewrite_log on;

    location / {

        if ($args ~* (.*)(next_page=http%3A)(.*)) {
            set $args $1next_page=https%3A$3;
            rewrite ^(.*)$ $1;
        }

        proxy_pass http://127.0.0.1:80;
        proxy_redirect http:// https://;

        proxy_set_header Host $host;
        proxy_set_header HTTPS "on";
        proxy_set_header X-Real-IP  $remote_addr;
        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;
        proxy_set_header X-Forwarded-Proto https;
        proxy_set_header X-Forwarded-Port 443;
    }
}

nginx_error.log:

2017/09/20 13:48:13 [notice] 25115#0: *1 "(.*)(next_page=http%3A)(.*)" matches "next_page=http%3A//example.com/f3/new_page", client: X.X.X.X, server: example.com, request: "GET /f1/f2/page?next_page=http%3A//example.com/f3/new_page HTTP/1.1", host: "example.com"
2017/09/20 13:48:13 [notice] 25115#0: *1 "^(.*)$" matches "/f1/f2/page", client: X.X.X.X, server: example.com, request: "GET /f1/f2/page?next_page=http%3A//example.com/f3/new_page HTTP/1.1", host: "example.com"
2017/09/20 13:48:13 [notice] 25115#0: *1 rewritten data: "/f1/f2/page", args: "next_page=https3A//example.com/f3/new_page", client: X.X.X.X, server: example.com, request: "GET /f1/f2/page?next_page=http%3A//example.com/f3/new_page HTTP/1.1", host: "example.com"
2017/09/20 13:48:13 [notice] 25115#0: *1 "(.*)(next_page=http%3A)(.*)" does not match "next_page=https3A//example.com/f3/new_page", client: X.X.X.X, server: example.com, request: "GET /f1/f2/page?next_page=http%3A//example.com/f3/new_page HTTP/1.1", host: "example.com"
2017/09/20 13:48:13 [notice] 25115#0: *1 "(.*)(next_page=http%3A)(.*)" does not match "", client: X.X.X.X, server: example.com, request: "GET /f1/f2/cookie/++resource++baseimg/regio.ico HTTP/1.1", host: "example.com", referrer: "https://example.com/f1/f2/page?next_page=http%3A//example.com/f3/new_page"

So the scheme get's changed by the proxy_redirect (I need to do this since occassionally the application itself will redirect to some http-URI), proxy_pass forwards it to the correct server and the args get changed, but the request does not. What am I missing here?

URL as shown in browser:

https://example.com/f1/f1/page?next_page=http%3A//example.com/f3/new_page

btw. nginx version is 1.10.1 and I am not able to upgrade it at this point

Vince
  • 51
  • 1
  • 1
  • 7

2 Answers2

3

So, what ended up working was changing

if ($args ~* (.*)(next_page=http%3A)(.*)) {
    set $args $1next_page=https%3A$3;
    rewrite ^(.*)$ $1;
}

proxy_pass http://127.0.0.1:80;
proxy_redirect http:// https://;

to

if ($args ~* (.*)(next_page=http%3A)(.*)) {
    set $args $1next_page=https%3A$3;
    rewrite ^.*$ $1 redirect;
}

proxy_pass http://127.0.0.1:80$uri$is_args$args;
proxy_redirect http:// https://;
Vince
  • 51
  • 1
  • 1
  • 7
1

One possible issue is that nginx might not allow setting over the variables it defines by itself. I don't know this for sure, it is an educated guess.

This means that you should use a different name for the arguments.

Even better is to use the map feature to get the new query arguments.

In the http level of configuration, add the following map:

map $args $newargs {
    default $args;
    ~^(.*)next_page=http:(.*)$ $1next_page=https:$2;
}

And in your server block, use the following location:

location / {
    proxy_pass http://127.0.0.1:80$uri$is_args$newargs;
    ...
}

Anyway, your setup looks kind of odd, since you are proxying to the http port, which I presume is running on nginx on the server... I would simply do a redirect to https on all requests to the http port.

Tero Kilkanen
  • 36,796
  • 3
  • 41
  • 63
  • 1) on port 80 there's another service listening, not nginx. We might change that, but not before this thing is running as it should. 2) nginx seems to allow that for args, see the log in my first post, as it changes those on throws no error. If I try to change the request_uri it errors with "does not allow to reset" etc. 3) mapping this way does not seem to work for me, nginx errors with "nginx: [emerg] unknown "1next_page=https%3a$2" variable" – Vince Sep 21 '17 at 07:28
  • 4) I can get nginx to start using your proposed changes if I change the map variable to ($1)next_page=https%3A($2) but I still end up without the args changed – Vince Sep 21 '17 at 07:43
  • The issue could be that nginx already uses the decoded form for the query string here, and therefore one cannot match with the URL encoded form here. However, I wasn't able to find any documentation about this, so you have to test with my changed `map` regular expression above. – Tero Kilkanen Sep 21 '17 at 12:19
  • Apparently it used the encoded form. I tested with both your suggestion and my original if($args...-approach (since this is a rewrtie and the log then shows me what it did) and while it did match the encoded form (see log from my question) it showed that it did not match the decoded form. – Vince Sep 21 '17 at 12:35