19

I'd like to have Tomcat automatically add a trailing slash to my app's context if the url is entered without it.

When I test with Jetty, it automatically adds the trailing slash to my app's context, but Tomcat doesn't do this.

I'm uncertain what the context will be named once deployed, as I'm handing the WAR off to someone else, so any resource references in HTML is all relative. Is there any way to have Tomcat automatically redirect to the same context with a trailing slash added?

Currently Using Tomcat 7 with Spring 3.

Badweather
  • 261
  • 1
  • 3
  • 9
  • Please add example of what exactly you want to have done, and clarify why the slash is required? – Thorbjørn Ravn Andersen Jun 15 '12 at 17:47
  • basically I need to have `http://url.com/context` load `http://url.com/context/` Tomcat add the trailing slash for all demo applications, but not for mine. When I run the app with Jetty, the trailing slash gets added automatically as well. The slash is required as `context/resources/` has image, css, js etc and they are using relative paths in the html, so `src="resources/image.jpg"` for example. – Badweather Jun 15 '12 at 20:04

5 Answers5

30

It's an old post, but as of Tomcat 7.0.67, you need to add the following attribute to your context.xml file:

<Context mapperContextRootRedirectEnabled="true">...</Context>

As per the 7.0.67 changelog:

Move the functionality that provides redirects for context roots and directories where a trailing / is added from the Mapper to the DefaultServlet. This enables such requests to be processed by any configured Valves and Filters before the redirect is made. This behaviour is configurable via the mapperContextRootRedirectEnabled and mapperDirectoryRedirectEnabled attributes of the Context which may be used to restore the previous behaviour.

And in the Tomcat context documentation:

mapperContextRootRedirectEnabled: If enabled, requests for a web application context root will be redirected (adding a trailing slash) if necessary by the Mapper rather than the default Servlet. This is more efficient but has the side effect of confirming that the context path exists. If not specified, the default value of false is used.

Pat
  • 411
  • 5
  • 4
  • Sneaky Tomcat! I just wasted two whole days trying to figure out what was wrong with my web app! Lesson relearned: if all else fails, RTFM! – Mark Jan 27 '16 at 08:53
  • This saves my life, I think the default value of mapperContextRootRedirectEnabled is true, so u don't need add them. – Yosua Lijanto Binar Sep 01 '16 at 16:33
  • 4
    Yes, default has been changed to "true" since 7.0.68 – Pat Sep 03 '16 at 07:47
  • This solved a problem for me. Spring MVC app on Tomcat running behind an F5 load balancer. – Kimball Robinson Nov 15 '16 at 22:02
  • According to the changelog I would have said since 7.0.66 : https://tomcat.apache.org/tomcat-7.0-doc/changelog.html#Tomcat_7.0.66_(violetagg) – Guillaume Husta Feb 14 '17 at 12:44
  • Guillaume 7.0.66 was never available for download. They jumped from 7.0.65 to 7.0.67. See https://archive.apache.org/dist/tomcat/tomcat-7/ – Pat Feb 15 '17 at 11:47
7

It seems that your application's web.xml has a mapping to "/*". A servlet-mapping to "/*" causes tomcat to pass the request as-is to the web application (i.e. does not redirect).

To properly redirect, you must change the "/*" mapping to just "/", the latter means the default servlet.

alexgirao
  • 885
  • 9
  • 9
  • 1
    +1 In my case, I've configured Spring Security to intercept `/*`. Is there any config available to Spring Security to add trailing slashes? – manikanta Apr 08 '15 at 03:00
5

Tomcat adds a trailing slash automatically. Just test it with the example application supplied with Tomcat..

If - due to some special configuration - it does not, I'd write a Filter that examines the query string and redirects as needed by the application. Many times this is needed anyways (doing http->https redirections, etc.)

Istvan Devai
  • 3,962
  • 23
  • 21
  • You are right, Tomcat does add these automatically. I need to figure out why this is being disabled in my application then. – Badweather Jun 15 '12 at 18:14
  • Is there some other server in front of Tomcat? Apache, nginx, etc? Those could mess up redirection.. – Istvan Devai Jun 15 '12 at 18:25
  • Actually, a rproxy server in front of it is able to fix the issue by adding trailing slash for me. I believe the issue has to do with Spring MVC accepting my context without a trailing slash as a valid url. – Badweather Jun 15 '12 at 18:30
  • 1
    Spring MVC only gets the request after the proxy and tomcat processed it (including the redirection). My gut feeling is that the proxy messes up the redirect, but you should check out the logs – Istvan Devai Jun 16 '12 at 13:00
  • This answer is outdated. See @Pat 's answer. – bekce Apr 27 '16 at 16:37
0

Have you tried playing with URL Rewrite on Tomcat?
This might help: http://code.google.com/p/urlrewritefilter/

If that does not help, take a look at this: URL rewrite in tomcat web.xml

Community
  • 1
  • 1
Ervi B
  • 770
  • 7
  • 16
0

Pat's excellent answer helped me dig up a few more details on this. It seems this is related to some quirks in some versions of Tomcat (Tomcat 7 at 7.0.67+, and Tomcat 8 between 8.29 and 8.37) having to do with session cookies and URL redirection.

The bottom line seems to be that if the java server creates path-specific session cookies with a slash at the end (like "/app_name/"), then the server must also do an automatic initial redirect (/app_name --> /app_name/) ... otherwise, the session cookie will not get sent with the request, and it will never look to the server like you have a valid session. The may cause a redirect loop from the app to the authentication.

There are configurations in Tomcat that control both behaviors, but as far as I can tell, they were essentially out-of-sync in these versions, such that one might get the cookie with the trailing slash, without getting the redirect. There are several related issues/changes in the Tomcat changelog: https://tomcat.apache.org/tomcat-8.0-doc/changelog.html

As Pat has already noted, this is resolved by adding this attribute to your app's Context element:

<Context mapperContextRootRedirectEnabled="true">
em_bo
  • 624
  • 7
  • 13