Reputation: 4724
We're installing some application running Tomcat 6 behind Nginx for different clients. Some of those installations are HTTP only, some HTTPS only, somewhere both. One of those installations has HTTP and HTTPS working on non-standard ports (8070 and 8071) due to lack of public IPs. Application at hand is displayed as an iframe in another app.
Tomcat redirects all HTTPS requests to HTTP (so nothing displayed in iframe due to browser restrictions for mixed content).
Iframe code:
<iframe src="/saiku-ui">
Tomcat's server.xml
:
<Connector port="8080" protocol="HTTP/1.1"/>
<!-- A bit later... -->
<Valve className="org.apache.catalina.valves.RemoteIpValve"
remoteIpHeader="x-forwarded-for"
protocolHeader="x-forwarded-proto"
/>
Nginx vhost:
server {
listen 80;
listen 443 ssl spdy;
location /saiku-ui {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://saiku-server; # This is upstream name
proxy_redirect off;
}
}
upstream saiku-server {
server ip.of.tomcat.server:8080;
}
Tomcat should listen on one single port for both HTTP and HTTPS requests.
If there will be two <Connector>
tags it will be much harder to configure Nginx.
Tomcat should not redirect between schemas.
listen 8071 ssl spdy;
).I've tried to add schema
and proxyPort
attributes to <Connector>
, after that Tomcat will always redirect from HTTP to HTTPS (at least it's better).
I can't google such a configuration and not experienced with Tomcat. Please help.
Upvotes: 6
Views: 12969
Reputation: 131
If you are using Spring and don't want to change the tomcat configuration, there is another solution based on this answer.
After a few hours of google, I found there is no standard solution like a officially supported configuration, refer this comment.
See the java doc HttpServletResponse.sendRedirect.
Sends a temporary redirect response to the client using the specified redirect location URL. This method can accept relative URLs; the servlet container must convert the relative URL to an absolute URL before sending the response to the client. If the location is relative without a leading '/' the container interprets it as relative to the current request URI. If the location is relative with a leading '/' the container interprets it as relative to the servlet container root.
HttpServletResponseWrapper
import org.apache.commons.lang.StringUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.IOException;
public class SendRedirectOverloadedResponseBasedOnXForwardedProtocol extends HttpServletResponseWrapper {
private HttpServletRequest request;
public SendRedirectOverloadedResponseBasedOnXForwardedProtocol(HttpServletRequest request,
HttpServletResponse response) {
super(response);
this.request = request;
}
public void sendRedirect(String location) throws IOException {
String xForwardedProtocol = request.getHeader("X-Forwarded-Protocol");
String host = request.getHeader("Host");
if (StringUtils.isNotBlank(xForwardedProtocol) && StringUtils.isNotBlank(host) && !isUrlAbsolute(location)) {
location = xForwardedProtocol + "://" + host + location;
}
super.sendRedirect(location);
}
public boolean isUrlAbsolute(String location) {
location = location == null ? "" : location;
return location.toLowerCase().startsWith("http");
}
}
Filter
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class SendRedirectBasedOnXForwardedProtocolFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
chain.doFilter(request, new SendRedirectOverloadedResponseBasedOnXForwardedProtocol((HttpServletRequest) request, (HttpServletResponse) response));
}
@Override
public void destroy() {
}
}
Upvotes: 0
Reputation: 4724
Actually what I want really is not possible, so it's required to have two separate Connector
tags and two upstreams in Nginx, like so:
Tomcat's server.xml
:
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
proxyPort="80"
/>
<Connector port="8443" protocol="HTTP/1.1"
connectionTimeout="20000"
proxyPort="443"
scheme="https" secure="true"
/>
Matching Nginx configuration:
server {
listen 80;
listen 443 ssl spdy;
location /saiku-ui {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://saiku-server-$scheme; # This is upstream name, note the variable $scheme in it
proxy_redirect off;
}
}
upstream saiku-server-http {
server ip.of.tomcat.server:8080;
}
upstream saiku-server-https {
server ip.of.tomcat.server:8443;
}
Please note that Tomcat receives plain HTTP traffic on both 8080 and 8443 ports (no SSL there, it's terminated by Nginx), but for connections on 8443 port it will generate links must start with https://
instead of http://
(via attributes scheme="https" secure="true"
) and will insert in links ports, specified in proxyPort
attribute.
Nginx will terminate SSL and proxy all secure connections to the 8443 port of Tomcat via saiku-server-https
upstream, where https
is the value of $scheme
Nginx request variable (see location
block)
Upvotes: 5