nyzm
nyzm

Reputation: 2825

Is there a way to get client's actual port in a rails web application?

I want to get client's actual port number(the port that router gave to client computer) in a rails web application.

Is it possible ?

Upvotes: 3

Views: 950

Answers (4)

Michał Zubrzycki
Michał Zubrzycki

Reputation: 885

On rails 6.1 works: request.headers['REMOTE_PORT'] Using using nginx + passenger

Upvotes: 0

sandre89
sandre89

Reputation: 5908

At least in Rails 5.2+, request.env["REMOTE_PORT"] is blank, so I downvoted the other answer.

After some digging, the way I managed to obtain this information was setting a custom header in nginx.

You probably have something like this in your nginx configuration:


upstream {{ param_app_name }} {
  server unix:///var/www/{{ param_app_name }}/shared/tmp/sockets/puma-{{ param_app_name }}.sock;
}

...


location / {
    try_files $uri @app;
  }

  location @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;
    proxy_set_header X-Client-Request-Port $remote_port; # << -- Add this to your nginx conf
    proxy_redirect off;
    proxy_pass http://{{ param_app_name }};
  }

As you can see, you can pass custom headers from nginx to your Rails backend, using proxy_set_header in the same server block you are using proxy_pass to invoke your backend.

The header name X-Client-Request-Port is arbitrary and you can choose whatever you like, but there's an old convention to use X-... for custom headers.

After setting this up and reloading your nginx, you can access this value in Rails simply by using request.headers["X-Client-Request-Port"].

By the way, I assume you are asking this for logging purposes. If that's the case, I suggest you take a look at Lograge gem, which will make your log entries a single line per request only, reducing the mess that Rails default logger is in production. I have it configured like this following Ankane's guide here:

In ApplicationController:

def append_info_to_payload(payload)
    super

    payload[:remote_ip] = request.remote_ip
    payload[:remote_port] = request.headers["X-Client-Request-Port"]
    payload[:user_id] = current_user.id     if current_user
    # You can add more custom stuff here, just whitelist it in production.rb below.
  end

Then, in config/environments/production.rb:

config.lograge.enabled = true

  config.lograge.custom_options = lambda do |event|
    options = event.payload.slice(
      :request_id,
      :user_id,
      :remote_ip,
      :remote_port,
    )
    options[:params] = event.payload[:params].except("controller", "action")
    options
  end

Upvotes: 1

nyzm
nyzm

Reputation: 2825

I was looking for something like request.remote_port since there is request.remote_ip

This is what I came up with request.env["REMOTE_PORT"]

Upvotes: 0

Matthias
Matthias

Reputation: 4375

Check out this question and answers:

How do I get the current absolute URL in Ruby on Rails?

This should be the trick:

"#{request.protocol}#{request.host_with_port}#{request.fullpath}"
"#{request.protocol}#{request.host}:#{request.port+request.fullpath}"

Upvotes: 0

Related Questions