After a POST, should I do a 302 or a 303 redirect? - http-status-codes

A common scenario for a web app is to redirect after a POST that modifies the database. Like redirecting to the newly created database object after the user creates it.
It seems like most web apps use 302 redirects, but 303 seems to be the correct thing to do according to the specification if you want the url specified in the redirect to be fetched with GET. Technically, with a 302, the browser is supposed to fetch the specified url with the same method that the original url was fetched with, which would be POST. Most browsers don't do that though.
302 - https://www.rfc-editor.org/rfc/rfc9110.html#name-302-found
303 - https://www.rfc-editor.org/rfc/rfc9110.html#name-303-see-other
So should I be using 302 or 303?

The correct one is 303.
I use it and haven't found any compatibility problems with UAs newer than Netscape 4 (1998, released 17 years ago).
If you use 302, you're risking that UA will re-send POST to the new URL instead of switching to GET.
Still, if you're worried about HTTP/1.0 clients (which don't support vhosts and probably won't be able to access your page anyway) then you should include HTML with link to the new page in body of the 303 response (web servers like Apache do that already).

Depends.
303 and 307 responses were added in HTTP1.1.
So client agents that are strictly compliant to HTTP1.1 RFC should be fine with a 303 response.
But there can be agents that are not fully conformant or are HTTP1.0 compliant and will not be able to handle the 303.
So to be sure that the response of your application can be handled gracefully by the majority of client implementations I think 302 is the safest option.
Excerpt from RFC-2616:
Note: Many pre-HTTP/1.1 user agents
do not understand the 303
status. When interoperability with such clients is a concern, the
302 status code may be used instead, since most user agents react
to a 302 response as described here for 303.

In most server-side languages the default redirection mechanism uses 302:
Java response.sendRedirect(..) uses 302
ASP.NET response.Redirect(..) uses 302
PHP header("Location: ..") uses 302
RoR redirect_to uses 302
etc..
So I'd prefer this, rather than setting status and headers manually.

In theory, you (and all the world) should be using 303 as you have noted. But also, most browsers react to a 302 like they should react to a 303. So, overall, it seems that it won't matter if you send 302 or 303. In the link you provided for the 303 specification, theres an interesting note:
Note: Many pre-HTTP/1.1 user agents do not understand the 303 status. When interoperability with such clients is a concern, the 302 status code may be used instead, since most user agents react to a 302 response as described here for 303.
It's important to note the pre-HTTP/1.1 user agents, so maybe this was important a while ago, but I don't believe it is the case now.
So, all in all, it's up to you (I could bet whatever you want that browsers won't change their behavior against 302 statuses never, for fear of breaking the internet for their users).

When providing the location of a new resource created by a POST request, 201 ("Created") is an appropriate response.
HTTP/1.1: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.2
Atom Publishing Protocol: https://www.rfc-editor.org/rfc/rfc5023#section-5.3
This does mean that a web browser probably won't redirect to the new URL, though; the user has to follow a link to get to the new item (this link can be provided in the body of the response, as well as in the Location header).

Related

OAuth 2.0, how to properly handle browser back button that navigates to an exchange endpoint?

I am implementing OAuth 2.0 protocol within my MVC application. Everything seem to working as expected except for one thing.
How do I handle a scenario with a browser back button when user is been redirected to their application after exchange end point has been completed? Problem is that browser caches state and code parameter and obviously code has already been exchanged.
I can always but some guard code to see if exception is thrown to send user back into original sign in flow that will redirect them to the default pages since there will be already SSO session. Not sure if this is a proper solution.
For information I am using Identity Server and mix of react, angular, mvc applications.
After doing more research I discovered that you can use 303 instead of 302 status code, in that case browsers should not not cache the first redirect per specification.
However based on the note in 302 section, most modern browsers implement 302 as if was 303 redirect and not caching the response.
The solution was, I started adding cache control header with no-cache value to my 302 redirects and browser does not take me back to the previous redirects.
Here is the documentation I took this from: https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
I used 302 instead of 303 because I have multiple redirects and documentation states that in 303 first redirect should not be cached meaning all the other it will ignore.

Beego Redirect with Post method

I want to redirect the url using the POST method.
The code is usually using this.Redirect("/", 302).
This time, I want something like post url which should redirect to that page.
Is this possible with Beego?
Thanks.
Beego's Redirect takes a http code, you're just using the wrong one. You want 307 to force the request method to be the same as the original one.
this.Redirect("/", 307)
Most clients and browsers issue a GET on the 302 target, which was actually contrary to the RFC.
Due to this, 303 and 307 were introduced:
303: clients must use a GET request
307: clients must use the original method (eg: POST if a POST was originally used)

Does a server have to carry out an operation before redirection?

In Is HTTP 303 acceptable for other HTTP methods? we established that HTTP 303 can be used for other HTTP methods.
The Post/Redirect/Get pattern requires the server to carry out an operation before returning HTTP 303. Is the same true for HTTP PUT and DELETE for this and other types of redirects? Is the server required to carry out the operation before redirection? Or can it assume that the client will repeat the request on the canonical URL as necessary?
This becomes even more interesting when you consider the fact that redirection is often used for load-balancing.
Quoting RESTful Web Services page 378:
303 ("See Other")
The request has been processed, but instead of the server sending a response document,
it’s sending the client the URI of a response document. This may be the URI to a static
status message, or the URI to some more interesting resource.
A few pages later...
307 (“Temporary Redirect”)
The request has not been processed, because the requested resource is not home: it’s
located at some other URI. The client should resubmit the request to another URI.
For GET requests, where the only thing being requested is that the server send a representation, this status code is identical to 303 (“See Other”). A typical case where 307 is a good response to a GET is when the server wants to send a client to a mirror site. But for POST, PUT, and DELETE requests, where the server is expected to take some
action in response to the request, this status code is significantly different from 303.
A 303 in response to a POST, PUT, or DELETE means that the operation has succeeded
but that the response entity-body is not being sent along with this request. If the client
wants the response entity-body, it needs to make a GET request to another URI.
A 307 in response to a POST, PUT, or DELETE means that the server has not even tried
to perform the operation. The client needs to resubmit the entire request to the URI in
the Location header.
An analogy may help. You go to a pharmacy with a prescription to be filled. A 303 is
the pharmacist saying “We’ve filled your prescription. Go to the next window to pick
up your medicine.” A 307 is the pharmacist saying “We can’t fill that prescription. Go
to the pharmacy next door.”

Missing POST Parameters with proxy servers

we encounter some strange behaviour with our web application. Some POST requests do not have any http body, when they should. content-length is 0. There are no post parameters at all. We traced the network traffic at our loadbalancer and we see that we do not get any request body with some of our POST requests.
All broken POST requests have in common that they arrive via a proxy server.
We already found this question on SO:
Why "Content-Length: 0" in POST requests?
We are now using a frame escape javascript routine and it helps a bit. It seems that error rate drops. But we still have POST requests with no data which should never happen in our webapp. These requests does not come from hackers or alike.
Often we saw webwasher as a proxy. But most of the time we do not see which proxy is used.
In this PDF we saw a comment about missing POST parameters with webwasher
WebWasher - Transparent Authentication Guide
Notes on Some Pitfalls
Note that there are some pitfalls that must be taken into account when setting up transparent authentication:
POST requests will fail if the ICAP server sends an redirect to the authentication server. This affects, however, only the renewal of the mapping since for the browser the request was successful, and the POST body will not be sent again after the final redirect.
We would like to know if there is some workaround other than using only GET instead of POST.
We would also here if other sites had problems with missing POST data and which conclusion they made.
Are there any other reasons why POST data is not sent?
I've had issues with Microsoft's proxy server not playing well with web requests.
I've had to resort to forcing HTTP/1.0 and setting the KeepAlive property to false.
There's something about the way NTLM authentication works that causes the body to be sent sporadically.
I've added this to many of my web requests
protected override WebRequest GetWebRequest(Uri uri)
{
HttpWebRequest webRequest = (HttpWebRequest) base.GetWebRequest(uri);
webRequest.KeepAlive = false;
webRequest.ProtocolVersion=HttpVersion.Version10;
return webRequest;
}
Hope this helps!
Not really an answer, I guess, but I arrived here because we had a similar problem. Initially, we thought it was due to the clients being mobile, as this was a common theme, but we have now realised that the common denominator is proxies.
We now raise a http 400 when it happens.
Here are a few of the proxies, we've had issues with. Posting them to lead the casual googler here:
1.1 ACISA02S, 1.1 abc:3328 (squid/2.6.STABLE21)
1.1 ipcop00.cat.local:8000 (squid/2.6.STABLE21)
1.1 PRXTGLSRV01
1.1 ISA
No ones which conform to the spec:
Some HTTP methods MUST cause a cache to invalidate an entity.
...
POST
(the HTTP/1.0 spec states 'Applications must not cache responses to a POST request').
But there are is a LOT of badly written code out there.
What headers do you include in replies to POSTs on the URLs?

Why "Content-Length: 0" in POST requests?

A customer sometimes sends POST requests with Content-Length: 0 when submitting a form (10 to over 40 fields).
We tested it with different browsers and from different locations but couldn't reproduce the error. The customer is using Internet Explorer 7 and a proxy.
We asked them to let their system administrator see into the problem from their side. Running some tests without the proxy, etc..
In the meantime (half a year later and still no answer) I'm curious if somebody else knows of similar problems with a Content-Length: 0 request. Maybe from inside some Windows network with a special proxy for big companies.
Is there a known problem with Internet Explorer 7? With a proxy system? The Windows network itself?
Google only showed something in the context of NTLM (and such) authentication, but we aren't using this in the web application. Maybe it's in the way the proxy operates in the customer's network with Windows logins? (I'm no Windows expert. Just guessing.)
I have no further information about the infrastructure.
UPDATE: In December 2010 it was possible to inform one administrator about this, incl. links from the answers here. Contact was because of another problem which was caused by the proxy, too. No feedback since then. And the error messages are still there. I'm laughing to prevent me from crying.
UPDATE 2: This problem exists since mid 2008. Every few months the customer is annoyed and wants it to be fixed ASAP. We send them all the old e-mails again and ask them to contact their administrators to either fix it or run some further tests. In December 2010 we were able to send some information to 1 administrator. No feedback. Problem isn't fixed and we don't know if they even tried. And in May 2011 the customer writes again and wants this to be fixed. The same person who has all the information since 2008.
Thanks for all the answers. You helped a lot of people, as I can see from some comments here. Too bad the real world is this grotesque for me.
UPDATE 3: May 2012 and I was wondering why we hadn't received another demand to fix this (see UPDATE 2). Looked into the error protocol, which only reports this single error every time it happened (about 15 a day). It stopped end of January 2012. Nobody said anything. They must have done something with their network. Everything is OK now. From summer 2008 to January 2012. Too bad I can't tell you what they have done.
UPDATE 4: September 2015. The website had to collect some data and deliver it to the main website of the customer. There was an API with an account. Whenever there was a problem they contacted us, even if the problem was clearly on the other side. For a few weeks now we can't send them the data. The account isn't available anymore. They had a relaunch and I can't find the pages anymore that used the data of our site. The bug report isn't answered and nobody complaint. I guess they just ended this project.
UPDATE 5: March 2017. The API stopped working in the summer of 2015. The customer seems to continue paying for the site and is still accessing it in February 2017. I'm guessing they use it as an archive. They don't create or update any data anymore so this bug probably won't reemerge after the mysterious fix of January 2012. But this would be someone else's problem. I'm leaving.
Internet Explorer does not send form fields if they are posted from an authenticated site (NTLM) to a non-authenticated site (anonymous).
This is feature for challange-response situations (NTLM- or Kerberos- secured web sites) where IE can expect that the first POST request immediately leads to an HTTP 401 Authentication Required response (which includes a challenge), and only the second POST request (which includes the response to the challange) will actually be accepted. In these situations IE does not upload the possibly large request body with the first request for performance reasons. Thanks to EricLaw for posting that bit of information in the comments.
This behavior occurs every time an HTTP POST is made from a NTLM authenticated (i.e. Intranet) page to a non-authenticated (i.e. Internet) page, or if the non-authenticated page is part of a frameset, where the frameset page is authenticated.
The work-around is either to use a GET request as the form method, or to make sure the non-authenticated page is opened in a fresh tab/window (favorite/link target) without a partly authenticated frameset. As soon as the authentication model for the whole window is consistent, IE will start to send form contents again.
Definitely related: http://www.websina.com/bugzero/kb/browser-ie.html
Possibly related: KB923155
Full Explanation: IEInternals Blog – Challenge-Response Authentication and Zero-Length Posts
This is easy to reproduce with MS-IE and an NTLM authentication filter on server side. I have the same issue with JCIFS (1.2.), struts 1. and MS-IE 6/7 on XP-SP2. It was finally fixed. There are several workarounds to make it up.
change form method from POST (struts default setting) to GET.
For most pages with small sized forms, it works well. Unfortunately i have possibly more than 50 records to send in HTTP stream back to server side. IE has a GET URL limit 2038 Bytes (not parameter length, but the whole URL length). So this is a quick workaround but not applicable for me.
send a GET before POST action executing.
This was recommended in MS-KB. My project has many legacy procedures and i would not take the risk at the right time. I have never tried this because it still needs some extra authentication processing when GET is received by filter layer based on my understanding from MS-KB and I would not like to change the behavior with other browsers, e.g. Firefox, Opera.
detecting if POST was sent with zero content-length (you may get it from header properties hash structure with your framework).
If so, trigger an NTLM authentication cycle by get challenge code from DC or cache and expect an NTLM response.
When the NTLM type2 msg is received and the session is still valid, you don't really need to authenticate the user but just forward it to the expected action if POST content-length is not zero. BTW, this would increase the network traffics. So check your cache life time setting and SMB session soTimeOut configuration before applying the change plz.
Or, more simple, you may just send a 401-unauthorized status to MS-IE and the browser shall send back POST request with data in reply.
MS-KB has provided a hot-fix with KB-923155 (I could not post more than one link because of a low reputation number :{ ) , but it seems not working. Would someone post a workable hot-fix here? Thanks :) Here is a link for reference, http://www.websina.com/bugzero/kb/browser-ie.html
We have a customer on our system with exactly the same problem. We've pin pointed it down to the proxy/firewall. Microsoft's IAS. It's stripping the POST body and sending content-length: 0. Not a lot we can do to work around however, and down want to use GET requests as this exposes usernames/passwords etc on the URL string. There's nearly 7,000 users on our system and only one with the problem... also only one using Microsoft IAS, so it has to be this.
There's a good chance the problem is that the proxy server in between implements HTTP 1.0.
In HTTP 1.0 you must use the Content-Length header field: (See section 10.4 here)
A valid Content-Length is required on
all HTTP/1.0 POST requests. An
HTTP/1.0 server should respond with a
400 (bad request) message if it cannot
determine the length of the request
message's content.
The request going into the proxy is HTTP 1.1 and therefore does not need to use the Content-Length header field. The Content-Length header is usually used but not always. See the following excerpt from the HTTP 1.1 RFC S. 14.13.
Applications SHOULD use this field to
indicate the transfer-length of the
message-body, unless this is
prohibited by the rules in section
4.4.
Any Content-Length greater than or
equal to zero is a valid value.
Section 4.4 describes how to determine
the length of a message-body if a
Content-Length is not given.
So the proxy server does not see the Content-Length header, which it assumes is absolutely needed in HTTP 1.0 if there is a body. So it assumes 0 so that the request will eventually reach the server. Remember the proxy doesn't know the rules of the HTTP 1.1 spec, so it doesn't know how to handle the situation when there is no Content-Length header.
Are you 100% sure your request is specifying the Content-Length header? If it is using another means as defined in section 4.4 because it thinks the server is 1.1 (because it doesn't know about the 1.0 proxy in between) then you will have your described problem.
Perhaps you can use HTTP GET instead to bypass the problem.
This is a known problem for Internet explorer 6, but not for 7 that I know of. You can install this fix for the IE6 KB831167 fix.
You can read more about it here.
Some questions for you:
Do you know which type of proxy?
Do you know if there is an actual body sent in the request?
Does it happen consistently every time? Or only sometimes?
Is there any binary data sent in the request? Maybe the data starts with a \0 and the proxy has a bug with binary data.
If the user is going through an ISA proxy that uses NTLM authentication, then it sounds like this issue, which has a solution provided (a patch to the ISA proxy)
http://support.microsoft.com/kb/942638POST requests that do not have a POST body may be sent to a Web server that is published in ISA Server 2006
I also had a problem where requests from a customer's IE 11 browser had Content-Length: 0 and did not include the expected POST content. When the customer used Firefox, or Chrome the expected content was included in the request.
I worked out the cause was the customer was using a HTTP URL instead of a HTTPS URL (e.g. http://..., not https://...) and our application uses HSTS. It seems there might be a bug in IE 11 that when a request gets upgraded to HTTPS due to HSTS the request content gets lost.
Getting the customer to correct the URL to https://... resulted in the content being included in the POST request and resolved the problem.
I haven't investigated whether it is actually a bug in IE 11 any further at this stage.
Are you sure these requests are coming from a "customer"?
I've had this issue with bots before; they sometimes probe sites for "contact us" forms by sending blank POST requests based on the action URI in FORM tags they discover during crawling.
Presence and possible values of the ContentLength header in HTTP are described in the HTTP ( I assume 1/1) RFC:
http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.13
In HTTP, it SHOULD be sent whenever the message's length can be determined prior to being transferred
See also:
If a message is received with both a
Transfer-Encoding header field and a Content-Length header field,
the latter MUST be ignored.
http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.4
Maybe your message is carrying a Transfer-Encoding header?
Later edit: also please note "SHOULD" as used in the RFC is very important and not equivalent to "MUST":
3. SHOULD This word, or the adjective "RECOMMENDED", mean that there
may exist valid reasons in particular circumstances to ignore a
particular item, but the full implications must be understood and
carefully weighed before choosing a different course.
Ref: http://www.ietf.org/rfc/rfc2119.txt
We had a customer using same website in anonymous and NTLM mode (on different ports). We found out that in our case the 401 was related to Riverbed Steelhead application used for http optimization. The first signal pointing us into that direction was a X-RBT-Optimized-By header. The issue was the Gratuitous 401 feature:
This feature can be used with both per-request and per-connection
authentication but it‘s most effective when used with per-request
authentication. With per-request authentication, every request must be
authenticated against the server before the server would serve the
object to the client. However, most browsers do not cache the server‘s
response requiring authentication and hence it will waste one
round-trip for every GET request. With Gratuitous 401, the client-side
Steelhead appliance will cache the server response and when the client
sends the GET request without any authentication headers, it will
locally respond with a ―401 Unauthorized‖ message and therefore saving
a round trip. Note that the HTTP module does not participate in the
actual authentication itself. What the HTTP module does is to inform
the client that the server requires authentication without requiring
it to waste one round trip.
Google also shows this as an IE (some versions, anyway) bug after an https connection hits the keepalive timeout and reconnects to the server. The solution seems to be configuring the server to not use keepalive for IE under https.
Microsoft's hotfix for KB821814 can set Content-Length to 0:
The hotfix that this article describes implements a code change in Wininet.dll to:
Detect the RESET condition on a POST request.
Save the data that is to be posted.
Retry the POST request with the content length set to 0. This prevents the reset from occurring and permits the authentication process to complete.
Retry the original POST request.
curl sends PUT/POST requests with Content-Length: 0 when configured to use HTTP proxy. It's trick to overcome required buffering in case of first unauthorized PUT/POST request to proxy. In case of GET/HEAD requests curl simply repeats the query. The scheme for PUT/POST is like:
Send first PUT/POST request with Content-Length set to 0.
Get answer. HTTP status code of 407 means we have to use proxy
authorization. Prepare headers for proxy authentication for send request.
Send request again with filled headers for proxy authentication and real data to POST/PUT.

Resources