HTTP redirects in a Turbo-Rails app
Tags: rails, hotwire, turbo, http
Published: 2022-09-15 12:39:27

_This post was extracted and adapted from [The Rails and Hotwire Codex](https://railsandhotwirecodex.com)._

In Rails, a conventional redirect in a controller action looks like:

```ruby
def create
  # ...

  redirect_to root_path
end
```

This sends a response with an HTTP status code of `302 Found`.

The [Turbo](http://turbo.hotwired.dev) [docs](https://turbo.hotwired.dev/handbook/drive#redirecting-after-a-form-submission), however, state that the response to a form submission must be `303 See Other` unless something goes wrong.

The reason behind this is the [spec](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/302) for `302 Found` states that the redirected request should use the same HTTP method as the original request. Due to legacy reasons, the [`fetch`](https://javascript.info/fetch) (used by Turbo under the hood) implementation in most browsers will respond to a `302 Found` response from a `POST` request by issuing a `GET` request to the redirected path. For all other HTTP methods, it will use the same method as the original request when redirecting.

Responding with a `303 See Other` status ensures that the method used for the redirect is always `GET`.

```ruby
def create
  # ...

  redirect_to root_path, status: :see_other
end
```

This anomaly with redirect codes isn't a massive problem in Rails because Rails forms use the only two browser native methods: `GET` and `POST`. Forms with different methods are rendered to use `POST` with the method inserted as a hidden field which is then parsed by Rails before the request hits a controller. 

For example:

```erb
<%= form_with(url: posts_path, method: :put) do |form| %>
  <%= form.text_field :name %>

  <%= form.submit %>
<% end %>
```

will render

```html
<form action="/posts" accept-charset="UTF-8" method="post">  
  <input type="hidden" name="_method" value="put">
  <input type="hidden" name="authenticity_token" value="...">

  <input type="text" name="name" id="name">

  <input type="submit" name="commit" value="Save">
</form>
```

However, this could potentially change as Rails integrates more tightly with Turbo. There are already a couple of use cases where `302` redirects can be problematic.

Turbo submits the form using the method specified in the `formmethod` attribute when it's defined. The below form would submit using a `PUT` request.

```erb
<%= form_with(url: posts_path) do |form| %>
  <%= form.text_field :name %>

  <%= form.submit formmethod: :put %>
<% end %>
```

If your controller redirected using a `302 Found`, that would result in another `PUT` request to the redirected path.

Since Turbo intercepts form submissions, you can use any HTTP method in a `<form>`'s `method` attribute, not just `GET` and `POST`. This form will also submit with a `PUT`:

```html
<form action="/posts" accept-charset="UTF-8" method="put">  
  <input type="text" name="name" id="name">

  <input type="submit" value="Save">
</form>
```

Rails form helpers won't render this, but it's good to know what's possible!

As such, I believe it's good practice to ALWAYS redirect with a `303 See Other` status code when using Turbo in a Rails app.

_If you liked this post, check out my book, [The Rails and Hotwire Codex](https://railsandhotwirecodex.com), to level-up your Rails and Hotwire skills!_