Submission formats

Redirect after a successful POST

Return a redirect after changing server state so refreshing the result page does not submit the same form again.

After a successful POST, don’t render the result directly. Redirect to a GET page instead. This is the Post/Redirect/Get pattern, and it fixes a problem every visitor has hit at least once.

The problem

Say the server answers a POST with 200 OK and the confirmation HTML. The address bar still shows the POST URL. If the person refreshes, the browser asks “Resend the form?” and, if they say yes, submits it again. Now you have two support requests, two orders, or two comments.

The fix

Imagine a form that creates a support request:

POST /support/requests

The server validates the fields, creates request 842, and instead of rendering anything responds with:

HTTP/1.1 303 See Other
Location: /support/requests/842

The browser follows the redirect on its own:

GET /support/requests/842

Now the address bar holds the result URL. Refreshing repeats a harmless GET. The page can be bookmarked and shared, as long as your access rules allow it. The POST is gone from history.

Why 303

There are several redirect status codes and they don’t all behave the same way. 303 See Other says explicitly: fetch this location with GET, whatever method you just used. That’s exactly what we want.

301 and 302 were written for GET and browsers usually switch to GET anyway, but that’s a habit, not a rule. 307 and 308 keep the original method, so they would repeat the POST at the new URL. Pick 303 on purpose.

Redirect only on success

When validation fails, don’t redirect. Return the form again with a 422 Unprocessable Content (or 400) status, the field errors, and the values the person entered. Redirecting an error throws all that away unless you stash it in a session first, which is more work for a worse result.

What a redirect doesn’t fix

It stops the refresh problem. It doesn’t stop duplicates in general. A person can double-click the button. A flaky network can retry the request. Someone can replay it with curl. If a second identical POST would cause damage, the server needs its own protection, like a unique token per form render or a database constraint. The redirect improves navigation. It’s not duplicate protection.

Try this: submit the form with the Network panel open. You should see the POST with a 303, then the GET for the new URL. Refresh the result page and confirm no second record appears.

Lesson completed