When a CSRF filter rejects a multipart claim upload, how does the redirect status it answers with affect the attached photographs?
answer
- where you send a refusal matters
- some redirects may rewrite the method
- POST becomes GET, the body is gone
- 307 and 308 preserve method and body
- a preserved body replays the refused value
basics
~20 s301 Moved Permanently and 302 Found permit a user agent to rewrite the POST as a GET, which discards the multipart body and the photographs with it. 307 Temporary Redirect and 308 Permanent Redirect preserve method and body - and therefore replay the value that was just refused.
solid answer
~50 sAnswering a refused submission with a redirect hands the outcome to the user agent's method rules. With `301 Moved Permanently` or `302 Found`, a user agent is permitted to change the request method from POST to GET when following it, and many do; the multipart body does not survive that rewrite, so the claimant lands on a page with their attachments silently gone. With `307 Temporary Redirect` or `308 Permanent Redirect` the method and body are preserved, which sounds better until you notice what the body contains: the same token that was just rejected, re-sent to the same check, possibly re-uploading megabytes to fail again. Neither is really what a token refusal wants. Answer the request directly with `403 Forbidden` and a re-rendered form, and keep redirects for cases where the target can actually succeed.
code
http · 5 linesHTTP/1.1 302 Found
Location: /claims/4812/upload
HTTP/1.1 307 Temporary Redirect
Location: /claims/4812/uploadgo deeper
Remember that a redirect is a second request, and that some redirect statuses let the user agent send it as a GET, which leaves no body to carry an upload.
Explain the split precisely: 301 and 302 permit a method rewrite, 307 and 308 are defined to preserve method and body. Say which one loses the photographs and why.
Judge both branches, not one. Show that preserving the body replays a refused value and re-transmits the upload, and conclude that a refusal should be answered directly rather than redirected.
Make the rejection path a design surface. Decide once, across the estate, what a refused write returns and what the user holds afterwards, so no team rediscovers this through a support ticket.
Where a filter sends a rejected request is a design decision, and on an upload form it decides whether the person has to choose their photographs again. Redirecting after a refusal is a habit carried over from post-then-redirect flows, and it interacts badly with both halves of this situation: a large body, and a value that is about to be replayed. ## What each redirect status does to the method | Status | What a user agent does with the method | What happens to the multipart body | |---|---|---| | `301 Moved Permanently` | may rewrite POST to GET when following | discarded with the rewrite | | `302 Found` | may rewrite POST to GET when following | discarded with the rewrite | | `307 Temporary Redirect` | defined to preserve the method | re-sent to the new target | | `308 Permanent Redirect` | defined to preserve the method | re-sent to the new target | The permissive wording on the first two is deliberate and historical: user agents rewrote POST to GET widely enough that the specification records the behaviour rather than forbidding it, and `307` and `308` exist precisely to give authors a way to say that the method must not change. If you write a `302` and reason about what happens next, you are reasoning about a behaviour that is permitted rather than guaranteed - which is its own argument against using one where the method matters. ## Why both branches fail the claimant **The rewriting branch.** The follow-up is a GET. There is no body, so there are no photographs and no description. The claimant sees the upload page again, often with no error at all if the redirect target is the form itself, and frequently believes the upload succeeded. A refusal that looks like a success is the worst outcome available here, because nothing prompts them to try again. **The preserving branch.** The follow-up is a POST carrying the identical body. Three things follow from that: 1. The same value that was just refused is submitted again, to the same check, so the second attempt fails for the same reason unless the redirect target has different rules - and a target with different rules for the same submission is a hole, not a fix. 2. The entire upload is transmitted a second time. On a slow uplink with several photographs that is a minutes-long round trip that ends in another refusal. 3. Some user agents ask the person to confirm re-sending a non-idempotent request, which turns an internal recovery step into a dialogue the claimant has no basis to answer. ## What to do instead - **Answer the request directly.** Return `403 Forbidden` with a reason code and, for a document-shaped client, the re-rendered form carrying a current value. There is no second request to lose anything on. - **Reserve the redirect for the success path**, where redirecting after a write is a genuinely useful pattern because the target can be reloaded safely. - **If you must redirect after a refusal, redirect to something that can succeed** - a page that re-renders the form - and accept that the attachments are gone, which means saying so in the interface rather than leaving the claimant to discover it. - **Do not carry the rejected submission forward** in the hope that a second attempt will pass. If the token was stale the remedy is a new form, not a replay. ## The wider point about the rejection branch This is a branch that almost never runs in development and runs constantly in production, which is why it collects defects: a redirect chosen for tidiness, a temporary file never deleted, a status that misleads the client, a fresh value issued into a response nobody reads. Treat the rejection path as a real path with a real user on it. Ask what the person holds after it completes, and on an upload form the answer should never be a page that looks fine and a claim with nothing attached to it.
- Why is 307 not the safe choice just because it preserves the upload?Because what it preserves includes the token that was just refused, so the replay hits the same check and fails the same way, after re-transmitting every attached photograph. Preservation is only valuable when the second attempt can succeed, and after a token refusal it cannot without a new value.
- What makes the rewriting case worse than an outright error page?It can look like success. The follow-up GET renders a normal page, often the form itself, with no error to read, so the claimant believes the documents were filed. A refusal the user cannot perceive is worse than a blunt one, because nothing prompts a retry.
- Is redirecting after a successful upload also a mistake?No - that is the case the pattern was invented for. After a write succeeds, redirecting to a page that can be reloaded safely stops a refresh from repeating the write. The mistake is carrying that habit onto the refusal branch, where the target cannot succeed and the body is still in flight.
saying these in an interview costs you the question
- Assumes every redirect preserves the POST method and body
- Thinks a 302 carries the multipart upload to the new target
- Believes replaying the identical body will pass the check next time
- Treats the choice between 302 and 307 as a matter of style
- Expects the browser to re-attach the chosen files after a GET