301 redirects are the correct way to signify content available at a URL has been permanently moved to a new location. Returning a 301 redirect instructs browsers, web crawlers and other clients accessing your site to use the new page from this point forward. From a development perspective a 302 redirect and a 301 don’t appear to be any different. You hit the old URL and you are sent to the new one.
301 Redirect Example
302 Redirect Example
In the above examples I’m using a manual HTTP GET on a set of URL’s I’ve created, one returns a 301 redirect and the other returns 302 redirect. Might not seem like much but when it comes to search engine optimization a 301 is ‘worth’ more than a 302. When a search engine like Google crawls your site and hits a 302 it (rightfully) assumes that the page is only temporarily moved. In the future it will still continue to access the old URL looking for the original page to come back. A result of this is that all the credit given to the old page by other sites linking to it will remain on the old page instead of being transferred to the new page. Google won’t transfer it to the new page unless you tell it the page has been permanently moved.
Grails as of 1.3.7 only supports 302 redirects. In Grails 2.0 this changes with the permanent parameter provided to a redirect. See http://grails.org/doc/2.0.x/ref/Controllers/redirect.html for more details. If you are still on 1.x, which I imagine you would be because at the time this article was written 2.0 isn’t stable yet, you have to do a bit more work.
Grails Filter
The only oddity I’ve found with the above code is you need to still keep a url mapping entry for the old URL. Failure to do so will result in a 404 before the filter is run. In the case of the above 301 I had to add “/301″(view: ‘doesntexist’) to my url mappings. The view doesn’t need to exist and since we are doing permanent redirect I would assume the controllers and views behind the old URL either don’t exist anymore or have been renamed/moved to to the new location.
Update: As per the conversation below with Mr. Andrew Taylor I think a more suitable solution would be as follows.
UrlMappings.groovy:
RedirectController.groovy:
This way you don’t have a list of URI’s to maintain in a filter. This will also work when Grails 2.0 provides the permanent attribute on redirects as a way to work around the Grails issue identified in the comments below. One should replace the body of the redirect301 closure with the proper controller redirect call at that time.
It would be a lot nicer if grails allowed (permanent) redirects directly in UrlMappings rather than requiring a fake mapping and a filter. Looks like there’s a feature request at http://jira.grails.org/browse/GRAILS-5994 but not on the roadmap for 2.0.
Definitely a more elegant solution for sure. I guess I could have implemented a simple RedirectController with the above Grails filter logic and did this UrlMappings.groovy:”/old” { controller = “redirect” newUri = “/new”}class RedirectController { static defaultAction = ‘redirect’ def redirect = { response.status = 301 …. }}Not as nice as “/old”(redirect: “/new”, permanent: true) though.