ChatGPT解决这个技术问题 Extra ChatGPT

What is correct HTTP status code when redirecting to a login page?

When a user is not logged in and tries to access a page that requires login, what is the correct HTTP status code for a redirect to the login page?

I am asking because none of the 3xx response codes set out by the W3C seem to fit the requirements:

10.3.1 300 Multiple Choices The requested resource corresponds to any one of a set of representations, each with its own specific location, and agent- driven negotiation information (section 12) is being provided so that the user (or user agent) can select a preferred representation and redirect its request to that location. Unless it was a HEAD request, the response SHOULD include an entity containing a list of resource characteristics and location(s) from which the user or user agent can choose the one most appropriate. The entity format is specified by the media type given in the Content- Type header field. Depending upon the format and the capabilities of the user agent, selection of the most appropriate choice MAY be performed automatically. However, this specification does not define any standard for such automatic selection. If the server has a preferred choice of representation, it SHOULD include the specific URI for that representation in the Location field; user agents MAY use the Location field value for automatic redirection. This response is cacheable unless indicated otherwise. 10.3.2 301 Moved Permanently The requested resource has been assigned a new permanent URI and any future references to this resource SHOULD use one of the returned URIs. Clients with link editing capabilities ought to automatically re-link references to the Request-URI to one or more of the new references returned by the server, where possible. This response is cacheable unless indicated otherwise. The new permanent URI SHOULD be given by the Location field in the response. Unless the request method was HEAD, the entity of the response SHOULD contain a short hypertext note with a hyperlink to the new URI(s). If the 301 status code is received in response to a request other than GET or HEAD, the user agent MUST NOT automatically redirect the request unless it can be confirmed by the user, since this might change the conditions under which the request was issued. Note: When automatically redirecting a POST request after receiving a 301 status code, some existing HTTP/1.0 user agents will erroneously change it into a GET request. 10.3.3 302 Found The requested resource resides temporarily under a different URI. Since the redirection might be altered on occasion, the client SHOULD continue to use the Request-URI for future requests. This response is only cacheable if indicated by a Cache-Control or Expires header field. The temporary URI SHOULD be given by the Location field in the response. Unless the request method was HEAD, the entity of the response SHOULD contain a short hypertext note with a hyperlink to the new URI(s). If the 302 status code is received in response to a request other than GET or HEAD, the user agent MUST NOT automatically redirect the request unless it can be confirmed by the user, since this might change the conditions under which the request was issued. Note: RFC 1945 and RFC 2068 specify that the client is not allowed to change the method on the redirected request. However, most existing user agent implementations treat 302 as if it were a 303 response, performing a GET on the Location field-value regardless of the original request method. The status codes 303 and 307 have been added for servers that wish to make unambiguously clear which kind of reaction is expected of the client. 10.3.4 303 See Other The response to the request can be found under a different URI and SHOULD be retrieved using a GET method on that resource. This method exists primarily to allow the output of a POST-activated script to redirect the user agent to a selected resource. The new URI is not a substitute reference for the originally requested resource. The 303 response MUST NOT be cached, but the response to the second (redirected) request might be cacheable. The different URI SHOULD be given by the Location field in the response. Unless the request method was HEAD, the entity of the response SHOULD contain a short hypertext note with a hyperlink to the new URI(s). 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. 10.3.5 304 Not Modified If the client has performed a conditional GET request and access is allowed, but the document has not been modified, the server SHOULD respond with this status code. The 304 response MUST NOT contain a message-body, and thus is always terminated by the first empty line after the header fields. The response MUST include the following header fields: - Date, unless its omission is required by section 14.18.1 If a clockless origin server obeys these rules, and proxies and clients add their own Date to any response received without one (as already specified by [RFC 2068], section 14.19), caches will operate correctly. - ETag and/or Content-Location, if the header would have been sent in a 200 response to the same request - Expires, Cache-Control, and/or Vary, if the field-value might differ from that sent in any previous response for the same variant If the conditional GET used a strong cache validator (see section 13.3.3), the response SHOULD NOT include other entity-headers. Otherwise (i.e., the conditional GET used a weak validator), the response MUST NOT include other entity-headers; this prevents inconsistencies between cached entity-bodies and updated headers. If a 304 response indicates an entity not currently cached, then the cache MUST disregard the response and repeat the request without the conditional. If a cache uses a received 304 response to update a cache entry, the cache MUST update the entry to reflect any new field values given in the response. 10.3.6 305 Use Proxy The requested resource MUST be accessed through the proxy given by the Location field. The Location field gives the URI of the proxy. The recipient is expected to repeat this single request via the proxy. 305 responses MUST only be generated by origin servers. Note: RFC 2068 was not clear that 305 was intended to redirect a single request, and to be generated by origin servers only. Not observing these limitations has significant security consequences. 10.3.7 306 (Unused) The 306 status code was used in a previous version of the specification, is no longer used, and the code is reserved. 10.3.8 307 Temporary Redirect The requested resource resides temporarily under a different URI. Since the redirection MAY be altered on occasion, the client SHOULD continue to use the Request-URI for future requests. This response is only cacheable if indicated by a Cache-Control or Expires header field. The temporary URI SHOULD be given by the Location field in the response. Unless the request method was HEAD, the entity of the response SHOULD contain a short hypertext note with a hyperlink to the new URI(s) , since many pre-HTTP/1.1 user agents do not understand the 307 status. Therefore, the note SHOULD contain the information necessary for a user to repeat the original request on the new URI. If the 307 status code is received in response to a request other than GET or HEAD, the user agent MUST NOT automatically redirect the request unless it can be confirmed by the user, since this might change the conditions under which the request was issued.

I'm using 302 for now, until I find the correct answer.

Update & conclusion:

HTTP 302 is better since its known to have best compatibility with clients/browsers.

I would say the absolutely by the book way would be to return a 401 and a login page without a redirect, but I'm not sure what your options are.
@Nick good point, but I would fear side effects from that if I were building a classic login system.
@Pekka - Absolutely agree, it depends on what platform this is on as to how all that can be cleanly handled, also if it's intranet vs internet comes into play I believe...you typically do authentication in a different way on an intranet, at least in my experience.
@Nick With 401 "The response MUST include a WWW-Authenticate header field" - How can i combine this with a MySQL database ? Isn't AuthType Basic and Digest limited to apache config files like .htpassword etc... ?
I want a custom login-page, not the basic browser-dialog asking for username and password...

P
Pekka

I'd say 303 see other 302 Found:

The requested resource resides temporarily under a different URI. Since the redirection might be altered on occasion, the client SHOULD continue to use the Request-URI for future requests. This response is only cacheable if indicated by a Cache-Control or Expires header field.

fits a login page most closely in my opinion. I initially considered 303 see other which would work just as well. After some thought, I'd say 302 Found is more fitting because the requested resource was found, there just is another page to go through before it can be accessed. The response doesn't get cached by default which is fine as well.


I agree, but i think 302 Found indicates that the resource was found, just under another url. Ex. I i want to se /my-messages/ the server answer with a 302 because "today" my messages are located in "/login/" (instead of "/messages/") ... I use 302, but I dont feel the context is 100% matching. Since the login-page is a different resource and not having the same content as requested.
@PHP_Jedi true. 303 may be more appropriate from that point of view. However, 302 is more reliable in terms of client compatibility.
Yep, I'm thinking that 303 might fit the the context better since it states "The response to the request can be found under a different URI". This is telling me that it is not the resource itself that is to be found in another URI, but only the response to this request.
303 states "This method exists primarily to allow the output of a POST-activated script...". That is not the case for me... Even if there was a POST, the login page does not output it. Ex if a user mark some messages in "/my-messages/" and POST a delete. If the session has timed out, the login page does not output any of these POST parameters.
@PHP_Jedi I'm not sure whether it's worth putting that much time into this. Both clients and servers in the http world have to be extremely liberal and fault-tolerant anyway, so there will be no real difference whether you use 302 or 303, except that 302 is better known. I find the level of detail commendable and it's always good to get things right, but too much effort may be futile in this specific area.
C
Community

This is a misuse of HTTP redirection mechanism. If user is not authorized then your app must return 401 Unauthorized. In case that the user is authorized but does not have an access to the requested resource then 403 Forbidden must be returned.

You should do the redirect on client side, e.g. by javascript. status code for redirection because required authorization does not exist. Using 30x for this does not conform to HTTP.

How to Think About HTTP Status Codes by Mark Nottingham

401 Unauthorized triggers HTTP’s request authentication mechanism.

401 Unauthorized status code requires presence of WWW-Authenticate header that supports various authentication types:

WWW-Authenticate: realm=

Bearer, OAuth, Basic, Digest, Cookie, etc

Hypertext Transfer Protocol (HTTP) Authentication Scheme Registry

Cookie-based HTTP Authentication - DRAFT


A 401 may not be appropriate in some cases as A server generating a 401 (Unauthorized) response MUST send a WWW-Authenticate header field (RFC), and not all login systems use that header.
Suppose you are refreshing a protected page ; client side javascript won't have any change to get called, and the browser will popup a login window instead of redirecting the user towards the login page - so the only way is to use a 30x code.
Golang cannot use 401 for redirection. That means we should use 30* for redirections.
@EIMEI following your reasoning, if another language or library forced you to use 401, then the Internet would be doomed. My point being: what you say points to a problem with Golang (although I find it surprising that it would have such a design to make it impossible to send 401s !)
@starbeamrainbowlabs There is a draft for Cookie-based HTTP Authentication as an option in the WWW-Authenticate header. See: tools.ietf.org/html/draft-broyer-http-cookie-auth-00
D
Davis Peixoto

I think the appropriate solution is the HTTP 401 (Not Authorized) header.

http://en.wikipedia.org/wiki/HTTP_codes#4xx_Client_Error

The purpose of this header is exactly this. But, instead of redirecting to a login page, the correct process would be something like:

User not logged try to access a login-restricted page.

system identifies user is not logged

system returns HTTP 401 header, AND display the login form in the same response (not a redirect).

This is a good practice, like providing a useful 404 page, with sitemap links, and a search form for example.

See you.


The RFC states: "The response MUST include a WWW-Authenticate header field (section 14.46) containing a challenge applicable to the requested resource." A 401 response is really only applicable when using an HTTP authentication scheme.
In that case 403 would be better since it states that the access is simply forbidden and authorization header wont help
@bshacklett WWW-Authenticate can be used together with many authentication schemes (e.g. Bearer, OAuth). See developer.mozilla.org/en-US/docs/Web/HTTP/Headers/… and iana.org/assignments/http-authschemes/http-authschemes.xhtml
There is a draft for Cookie-based HTTP Authentication as an option in the WWW-Authenticate header. See: tools.ietf.org/html/draft-broyer-http-cookie-auth-00
@aef Great point! But the draft for HTML-and-Cookie based authentication is from 2009 and never approved. Therefore, 401 is still not a suitable redirect code for HTML-and-Cookie based authentication.
G
GlenPeterson

Lots of compelling and contradictory responses here! How to choose?

Finalists:

302 Found

Indicates that the resource requested has been temporarily moved to the URL given by the Location header. A browser redirects to this page but search engines don't update their links to the resource. The specification requires the method (and the body) not be altered when the redirection is performed, but not all user-agents conform. It is therefore recommended to set the 302 code only as a response for GET or HEAD methods and to use 307 Temporary Redirect instead, as the method change is explicitly prohibited in that case. https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/302

Vague, possibly buggy, and maybe outdated, but could work, especially for GET requests.

307 Temporary Redirect

The resource requested has been temporarily moved to the URL given by the Location header. The only difference between 307 and 302 is that 307 guarantees that the method and the body will not be changed when the redirected request is made. With 302, some old clients were incorrectly changing the method to GET: the behavior with non-GET methods and 302 is then unpredictable on the Web, whereas the behavior with 307 is predictable. For GET requests, their behavior is identical. https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/307

My original take was that if your application supports HTTP 1.1+, this would be the newer, clearer way to make a temporary redirect.

401 Unauthorized

indicates that the client request has not been completed because it lacks valid authentication credentials for the requested resource. This status code is sent with an HTTP WWW-Authenticate response header that contains information on how the client can request for the resource again after prompting the user for authentication credentials. This status code is similar to the 403 Forbidden status code, except that in situations resulting in this status code, user authentication can allow access to the resource.

I agree this was probably intended to be the correct response code to say, "Not until you sign in." But almost every webapp uses HTML forms with Cookies to sign-in and this HTTP code doesn't allow "Cookie" as an authentication scheme: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/WWW-Authenticate and https://www.iana.org/assignments/http-authschemes/http-authschemes.xhtml

A few people have pointed out that someone drafted a "Cookie" scheme in 2009: WWW-Authenticate Cookie https://datatracker.ietf.org/doc/html/draft-broyer-http-cookie-auth-00 Not sure why that didn't fly. Maybe because that's outside the realm of HTTP?

Without "Cookie" as an option, I think this is not suitable for HTML-and-Cookie based authentication.

Survey

In the absence of an undeniable single "correct" answer, I looked at what a few industry leaders were doing.

Google: 302 Redirect to Sign In Page

Request URL: https://mail.google.com/mail/u/0/?tab=cm Request Method: GET Status Code: 302 Response: content-type: text/html; charset=UTF-8 location: https://accounts.google.com/ServiceLogin?service=mail&passive=1209600&osid=1&continue=https://mail.google.com/mail/u/0/?tab%3Dcm&followup=https://mail.google.com/mail/u/0/?tab%3Dcm&emr=1

Microsoft: 302 Redirect to Sign In page

Request URL: https://outlook.live.com/mail/0/?authRedirect=true&state=0 Request Method: GET Status Code: 302 Response: Location: https://outlook.live.com/owa/0/?state=1&redirectTo=aHR0cHM6Ly9vdXRsb29rLmxpdmUuY29tL21haWwvMC8

Facebook: 302 Redirect to Sign In Page

Request URL: https://www.facebook.com/friends Request Method: GET Status Code: 302 Response: location: https://www.facebook.com/login.php?next=https%3A%2F%2Fwww.facebook.com%2Ffriends

Twitter: 200 with interstitial sign-in page

Request URL: https://twitter.com/messages/ Request Method: GET Status Code: 200 (from service worker)

Me: 307: Temporary Redirect

I've used this for over a decade and never had a problem. Not suggesting that others adopt it, just saying it works as advertised as a 302 alternative in every major browser. I could be convinced to switch to using 302 as a result of researching this answer.

Conclusion

302 is the de-facto standard for redirects, unless you require a POST to be redirected to another POST, which I didn't try.

Really, I think a POST should either succeed or fail outright. POST causes a state change on the server (such as adding a product to inventory) and you really want to know if the change was processed and how many times. Redirecting would raise the question of whether your change was accepted or not before the redirect. Once you decide to NEVER redirect a POST, then 302 and 307 are equivalent. The "bugs" in 302 were in unrealistic use cases. Therefore, 307 should never have been created. Also 302 is more popular, so likely to be best supported.

The nice thing about using a single code for all temporary redirects is that you don't have to worry about the reason for the redirect.

200 with JavaScript throwing up an interstitial sign-in page is another option, probably more suited to single-page webapps. You asked for the page, you got the page, but you're going to be forced to sign in before you can see the contents.