Docker: rails redirect to non standard ports

Introduction

When you set up a development environment using docker, you sometimes want to test your rails application with an nginx proxy. When using a non standard port (not 80 or 443), you get into the minor issue that nginx listens to port 80 in the container, but you map a different port in your docker-compose.yml file.

  nginx:
    image: nginx:latest
    ports:
      - 8088:80
      - ./contrib/nginx.dev.conf:/etc/nginx/conf.d/default.conf

My nginx configuration

upstream docdeposit {
        server docdeposit:3000;
}

server {
    #--- here lays dragons
    listen 80;
    server_name docdepos.it;

    location / {
        proxy_pass http://docdeposit;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

}

Debugging your Rails application

class ApplicationController < ActionController::Base
...

  def authorized
    if signed_in?
      true
    else
      ap request.env
      redirect_to '/login'
    end
  end
end


# Testing the default behavior

So when testing my application I want to be redirected to http://localhost:8085/login.

```bash
$ curl -v http://localhost:8085
*   Trying 127.0.0.1:8085...
* Connected to localhost (127.0.0.1) port 8085 (#0)
> GET / HTTP/1.1
> Host: localhost:8085
...
< HTTP/1.1 302 Found
...
< Location: http://localhost/login
...
<html><body>You are being <a href="http://localhost/login">redirected</a>.</body></html>

The debug output shows us that the application gets from the nginx server through the http headers.

                                             "SERVER_PROTOCOL" => "HTTP/1.0",
                                                   "HTTP_HOST" => "localhost",
                                        "HTTP_X_FORWARDED_FOR" => "172.26.0.1",
                                      "HTTP_X_FORWARDED_PROTO" => "http",
                                             "HTTP_CONNECTION" => "close",
                                             "HTTP_USER_AGENT" => "curl/7.81.0",
                                                 "HTTP_ACCEPT" => "*/*",
                                      "puma.request_body_wait" => 0.0034820139408111572,
                                                 "SERVER_NAME" => "localhost",

                                                 "SERVER_PORT" => "80",

                                                   "PATH_INFO" => "/",

So, now we change the following:

  • Simple change in the docker-compose.yml file
  • One small change to the default.conf nginx configuration

In the docker-compose.yml file, you can either reference to a variable set in your .env file, or hard code it. I call the variable NGINX_PORT:

version: "3.9"

services:
  nginx:
    image: nginx:latest
    links:
      - docdeposit
    ports:
      - ${NGINX_PORT:-80}:80
    volumes:
      #- ./contrib/nginx.dev.conf:/etc/nginx/conf.d/default.conf
      - ./contrib/nginx.dev.conf:/etc/nginx/templates/default.conf.template
    environment:
      #--- hard coded
      #NGINX_PORT: "8085"

      #--- Dynamicaly override the NGINX port in the config, so that rails redirect works properly
      #--- in your local .env file, by setting the variable NGINX_PORT to something
      #--- that is free on your computer (i.e `NGINX_PORT=8085`)
      NGINX_PORT: "${NGINX_PORT:-80}"

The minor change in your nginx.dev.conf/default.conf file:

upstream docdeposit {
        server docdeposit:3000;
}

server {
    #--- here lays dragons
    listen 80;
    server_name docdepos.it;

    location / {
        proxy_pass http://docdeposit;

        # proxy_set_header Host $host;
        proxy_set_header Host $host:${NGINX_PORT};

        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

}

This is what you want to see, a proper redirect without having to reconfigure your rails application, only using the headers passed on from nginx:

(venv) [maglub@Magnuss-MBP-2:~/dev/kmg/demo/docdeposit (main)]$ curl -v http://localhost:8085
*   Trying 127.0.0.1:8085...
* Connected to localhost (127.0.0.1) port 8085 (#0)
> GET / HTTP/1.1
> Host: localhost:8085
...
< HTTP/1.1 302 Found
...
< Location: http://localhost:8085/login
...
<html><body>You are being <a href="http://localhost:8085/login">redirected</a>.</body></html>

And this is what the debug output from the rails application looks like:

                                             "SERVER_PROTOCOL" => "HTTP/1.0",
                                                   "HTTP_HOST" => "localhost:8085",
                                        "HTTP_X_FORWARDED_FOR" => "172.26.0.1",
                                      "HTTP_X_FORWARDED_PROTO" => "http",
                                             "HTTP_CONNECTION" => "close",
                                             "HTTP_USER_AGENT" => "curl/7.81.0",
                                                 "HTTP_ACCEPT" => "*/*",
                                      "puma.request_body_wait" => 0.0033560097217559814,
                                                 "SERVER_NAME" => "localhost",

                                                 "SERVER_PORT" => "8085",

                                                   "PATH_INFO" => "/",

References