Add new comment

Keeping real user IP in Java web apps behind Nginx proxy

Submitted by lennart on Wed, 07/06/2011 - 15:51

When there is an Nginx proxy in front of your tomcat (or other app server), request.getRemoteAddr() or request.getRemoteHost() will return the IP of the Nginx server. And this is obviously not what we want.

To get this right, an extra HTTP Header will have to be added in Nginx, and our web app will have to be told to use it.

Nginx configuration

Make sure the header X-Real-IP is added in your proxy configuration:

location /webapp/ {
  proxy_pass        http://localhost:8080/webapp/;
  proxy_set_header  X-Real-IP  $remote_addr;
}

Like mentioned on the Nginx wiki.

Web app configuration

To make sure the web app uses this header, the ServletRequest object has to be wrapped in a custom HttpServletRequestWrapper, in which the methods getRemoteAddr and getRemoteHost are overridden. The wrapping itself is done in a Filter.

The wrapper class:

package be.lacerta.web;

import java.net.InetAddress;
import java.net.UnknownHostException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

class RealIPRequestWrapper extends HttpServletRequestWrapper {
	public RealIPRequestWrapper(HttpServletRequest request) {
		super(request);
	}
	
	@Override
	public String getRemoteAddr() {
		String realIP = super.getHeader("X-Real-IP");
		return realIP!=null?realIP:super.getRemoteAddr();
	}
	
	@Override
	public String getRemoteHost() {
		try {
			return InetAddress.getByName(getRemoteAddr()).getHostName();
		} catch (UnknownHostException e) {
			return getRemoteAddr();
		}
	}
}

The filter class:

package be.lacerta.web;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class RealIPFilter implements Filter {

	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
		if (request instanceof HttpServletRequest) {			
			chain.doFilter(new RealIPRequestWrapper((HttpServletRequest)request), response);
		} else {
			chain.doFilter(request, response);
		}
	}

	@Override
	public void destroy() {
	}

	@Override
	public void init(FilterConfig arg0) throws ServletException {
	}
}

And add the filter in web.xml:

<filter>
        <filter-name>RealIPFilter</filter-name>
        <filter-class>be.lacerta.web.RealIPFilter</filter-class>
</filter>
<filter-mapping>
        <filter-name>RealIPFilter</filter-name>
        <url-pattern>/*</url-pattern>
</filter-mapping>

And we're good to go. Wherever request.getRemoteAddr() is used, our web app will be using the value from the X-Real-IP header, if available.

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.