Writing in the front

Nginx is the most popular reverse proxy and load balancing server that is widely used in Internet projects. This is not only because Nginx itself is relatively lightweight, but also because of the high performance features of Nginx and the support for plug-in development. For this reason, many developers and companies have developed many high performance plug-ins based on Nginx. Users can specify a plugin for Nginx to enhance the functionality or performance of Nginx in a particular scenario.

Nginx gets client information

Note: The client information in this document refers to the real IP address, domain name, protocol, and port of the client.

After Nginx reverse proxy, the Servlet application obtains the IP address of Nginx from request.getremoteaddr (), not the real IP address of the client. The domain name, protocol, and port obtained from request.getrequestURL () are the domain name, protocol, and port used by Nginx to access the Web application, rather than the real domain name, protocol, and port displayed in the address bar of the client browser.

What are the problems with direct access to information?

For example, on a server whose IP address is 192.168.1.100, Jetty or Tomcat port number is 8080, Nginx port number is 80, and Nginx reverse proxy port 8080:

server { listen 80; Location / {proxy_pass http://127.0.0.1:8080;# Reverse proxy application server HTTP address}}Copy the code

On another machine in the browser open http://192.168.1.100/test access a Servlet applications, access to the client IP and URL:

System.out.println("RemoteAddr: " + request.getRemoteAddr());
System.out.println("URL: " + request.getRequestURL().toString());
Copy the code

The following information is displayed:

RemoteAddr: 127.0.0.1
URL: http://127.0.0.1:8080/test
Copy the code

It can be found that the client IP obtained by the Servlet program is the IP address of Nginx instead of the IP address of the browser machine, and the URL obtained is the URL configured by Nginx proxy_pass instead of the real address in the browser address bar. If Nginx is used as the HTTP service at the back end of the HTTPS server reverse proxy, request.getrequestURL () gets the URL with an HTTP prefix rather than an HTTPS prefix, and cannot obtain the actual protocol in the browser address bar. If the URL obtained from Request.getrequestURL () is used as the concatenated Redirect address, the wrong Redirect will occur, which is a common problem with Nginx reverse proxies.

How to solve these problems?

Since there is a problem with using Nginx directly to get client information, how can we solve this problem?

As a whole, we need to solve these problems from two aspects:

(1) Since Nginx is a proxy server, all client requests are forwarded from Nginx to Jetty/Tomcat. If Nginx does not tell Jetty/Tomcat the real IP address, domain name, protocol, and port of the client, the Jetty/Tomcat application will never know this information. So Nginx needs to configure some HTTP headers to tell the proxied Jetty/Tomcat this information;

(2) The Jetty/Tomcat side can no longer get information from the client directly connected to it (i.e., Nginx). Instead, it needs to get information from the HTTP Header passed by Nginx.

The specific practices

Configure nginx

First, we need to add the following configuration to the Nginx configuration file nginx.conf.

proxy_set_header Host $http_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;
Copy the code

The following table describes the meanings of parameters.

  • HostContains the real domain name and port number of the client.
  • X-Forwarded-ProtoRepresents the real protocol of the client (HTTP or HTTPS);
  • X-Real-IPIndicates the real IP address of the client.
  • X-Forwarded-ForThe Header andX-Real-IPSimilar, but it contains the IP of the real client and each proxy server in between at multiple levels of proxy.

At this point, try the output of request.getremoteaddr () and request.getrequestURL () again:

RemoteAddr: 127.0.0.1
URL: http://192.168.1.100/test
Copy the code

You can see that the URL seems to be fine, but the IP is still the local IP and not the real client IP. However, if you use Nginx as the HTTPS server to reverse proxy the HTTP server, you will find that the browser address bar is HTTPS prefix, but the URL obtained from Request.getrequestURL () is still HTTP prefix, that is, only configuring Nginx cannot solve the problem completely.

Obtain client information using Java methods

Configuring Nginx alone won’t fix the problem, so what can be done to fix it? One solution is to obtain client information through Java methods, such as the following.

/*** * Obtain the client IP address. Nginx fetch is used here; X-Real-IP */
public static String getClientIP(HttpServletRequest request) {
    String fromSource = "X-Real-IP";
    String ip = request.getHeader("X-Real-IP");
    if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
    	ip = request.getHeader("X-Forwarded-For");
    	fromSource = "X-Forwarded-For";
    }
    if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
    	ip = request.getHeader("Proxy-Client-IP");
    	fromSource = "Proxy-Client-IP";
    }
    if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
    	ip = request.getHeader("WL-Proxy-Client-IP");
    	fromSource = "WL-Proxy-Client-IP";
    }
    if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
    	ip = request.getRemoteAddr();
    	fromSource = "request.getRemoteAddr";
    }
    return ip;
}
Copy the code

This is a good way to get the client’s IP address, but I’ve always found it unfriendly, because since the Servlet API provides the Request.getremoteaddr () method to get the client’s IP address, the presence or absence of a reverse proxy should be transparent to the author.

Next, I’ll show you how to configure the Jetty server and Tomcat server to get client information in a more user-friendly way.

The Jetty server

In the jetty. XML file of the Jetty server, go to httpConfig and add the configuration:

<New id="httpConfig" class="org.eclipse.jetty.server.HttpConfiguration">.<Call name="addCustomizer">
    <Arg><New class="org.eclipse.jetty.server.ForwardedRequestCustomizer"/></Arg>
  </Call>
</New>
Copy the code

Restart the Jetty, reoccupy browser open http://192.168.1.100/test test, result:

RemoteAddr: 192.168.1.100
URL: http://192.168.1.100/test
Copy the code

The IP obtained from request.getremoteaddr () is not 127.0.0.1, but the real IP of the client. The URL obtained from Request.getrequestURL () is also the real URL of the browser. If Nginx serves as an HTTPS proxy, Request.getrequesturl () will also be prefixed with HTTPS.

Additionally, Jetty wraps this functionality into a module: HTTP-Forwarded.Http. If you don’t want to change the jetty. XML configuration file, you can also enable the HTTP-Forwardedmodule.

For example, Jetty can be launched from the command line:

java -jar start.jar --module=http-forwarded
Copy the code

More Jetty how to enable the module information can refer to: www.eclipse.org/jetty/docum…

Tomcat

Similar to Jetty, if Tomcat is used as the application server, you can configure Tomcat’s server. XML file to add the Host element last:

<Valve className="org.apache.catalina.valves.RemoteIpValve" />
Copy the code

Ok, let’s call it a day! Don’t forget to give a look and forward, let more people see, learn together progress!!

Write in the last

If you think glacier wrote good, please search and pay attention to “glacier Technology” wechat public number, learn with glacier high concurrency, distributed, micro services, big data, Internet and cloud native technology, “glacier technology” wechat public number updated a large number of technical topics, each technical article is full of dry goods! Many readers have successfully moved to Dachang by reading articles on the “Glacier Technology” wechat official account; There are also many readers to achieve a technological leap, become the company’s technical backbone! If you also want to like them to improve their ability to achieve a leap in technical ability, into the big factory, promotion and salary, then pay attention to the “Glacier Technology” wechat public account, update the super core technology every day dry goods, so that you no longer confused about how to improve technical ability!