Rocket: Redirect all paths containing trailing slashes

Created on 23 Jan 2020  路  6Comments  路  Source: SergioBenitez/Rocket

(A similar question has been asked before (https://github.com/SergioBenitez/Rocket/issues/702) but only asked about a single path.)

How can I redirect any path containing a trailing slash to the same path but without the slash?

It's standard practice to avoid duplicate URLs with public facing webpages by redirecting them to either all include or all exclude a trailing slash. Manually writing a redirect for every existing path that could contain a trailing slash is generally unfeasible. I would like to match all paths that contain a trailing slash and perform a 301 redirect to the same path, but without the slash.

Including the trailing slash in the request route like so:

#[ get( "/<path..>/" ) ]
fn catch_all( path: PathBuf ) -> Redirect {
    ...
}

throws the error paths cannot contain empty segments which is understandable. Is it possible to match all paths with a trailing slash, and if so, how?

I'm aware I could do this with a proxy server like nginx, but I'm working on a fairly small project and would like to do it in app if possible.

Using Rocket 0.4.2

question

Most helpful comment

I wanna point out that this is not just a SEO issue, but it also breaks all relative paths in a returned html-page. A <img src="dog.jpg"> at http://example.org/somewhere requests https://example.org/dog.jpg, at http://example.org/somewhere/ requests https://example.org/somewhere/dog.jpg. So serving the same page under the two uris, with and without trailing slash, really breaks the page!
This just gave me some serious issues, so I think this is more than just a question.

All 6 comments

Is it possible to match all paths with a trailing slash, and if so, how?

I believe you will need a fairing in order to check every request like this. Unfortunately request fairings cannot directly respond to a request, so you will need a workaround. One possibility (e.g. as used by rocket_cors) is to change the request URI inside of on_request to an internal #[get] route, which would performs the actual redirect.

Since on_request fires for every request, you will have to check that request.uri() actually has a trailing slash before doing that redirect.

Also related to #1198 .

Another option is to implement a custom handler, which can do whatever it wants, that matches on all paths and redirects to a /-less path if the path ends in /. This would be as simple as:

impl Handler for Redirector {
    fn handle<'r>(&self, req: &'r Request, _: Data) -> Outcome<'r> {
        if req.uri().path().ends_with('/') {
            Outcome::from(req, Redirect::to(req.uri().to_normalized()))
        } else {
            Outcome::forward(data)
        }
    }
}

You'll also need an Into<Vec<Route>> implementation. See the Handler docs for a complete example.

I wanna point out that this is not just a SEO issue, but it also breaks all relative paths in a returned html-page. A <img src="dog.jpg"> at http://example.org/somewhere requests https://example.org/dog.jpg, at http://example.org/somewhere/ requests https://example.org/somewhere/dog.jpg. So serving the same page under the two uris, with and without trailing slash, really breaks the page!
This just gave me some serious issues, so I think this is more than just a question.

Yes, that's why I made a PR! See #1253 .

I believe this is now resolved by #1253! Closing.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

paulvt picture paulvt  路  4Comments

haheute picture haheute  路  4Comments

sphinxc0re picture sphinxc0re  路  3Comments

GoRustafari picture GoRustafari  路  3Comments

incker picture incker  路  3Comments