HTTP跳转状态码的研究

一直以来都是直接用PHP的header函数输出一个Location标头来实现跳转。很少深究这个跳转具体是怎么实现的,只知道是PHP看到有Location就生成一个302服务器返回状态码。

今天看了HTTP/1.1的协议标准文档,发现除了302 Found外,可以用于跳转的还有301 Moved Permanently、303 See Other以及307 Redirect Temporary。那么这几个状态码有什么不同呢?

顾名思义,301是实现永久跳转的。服务器返回这个状态码,相当于告诉来访者目标地址已经永久转移了,转移后的目标地址用Location标头来描述。由于是永久转移,中间的代理服务器和浏览器都可以缓存这个转移后的目标地址,以后用户再访问的时候,可以不把Request发到服务器上,直接导向目标地址。

302 Found的本意是通知浏览器去请求另一个资源,在HTTP/1.0的定义中,浏览器收到302返回码后,应该用和原请求方法相同的方法来请求目标地址。也就是说,如果浏览器POST到login.php上,而login.php最终返回302,并在Location标头中指定index.php,浏览器就应该再发个POST请求到index.php上。

但是实际情况是,早期版本的浏览器都会用GET方法来发起第二次请求。而早期的Web开发人员也在无数的地方写了大量的302返回码,目的是为了让浏览器跳转并用GET用GET访问。所以最终HTTP/1.1将错就错,加入了303 See Other和307 Redirect Temporary两个状态码。

303 See Other要求用GET方法访问Location中指定的地址。代理服务器或者浏览器可以根据返回标头中的缓存策略对结果加以缓存。现今的大多数浏览器使用处理303的方法来处理302,以保证向前兼容。而反过来,为了兼容早期的浏览器,Web开发人员应该尽量使用302来实现临时重定向。

307 Redirect Temporary就是当年真正的302了,它要求浏览器以完全相同的方法请求Location中指定的地址,而且绝对不可以缓存请求的结果。

现在应该很少见只支持HTTP/1.0的浏览器了,不过网上大量的爬虫程序未必能完整的实现HTTP/1.1,所以平时开发的时候还是多用301和302吧。如果遇到真的需要浏览器重新POST到新地址的情况,用一下307应该也不是问题。

Continue Reading

HTTP协议中GET/POST/COOKIE数据传递方式的对比

GET

GET方式传递数据,数据直接写在URL中,用一个英文问号分隔,然后以name=value的形式描述。为了避免和URL分隔符冲突,value部分的数据还需要进行urlencode转义处理。下面给出了一个典型的GET方式传递数据的例子:

http://guangxin.name/?p=772

问号后面的p=772就是GET方式传递的数据。对于PHP程序来说,GET方式传递的数据会被初始化成$_GET全局变量。对于大多数浏览器而言,GET方式传递的数据会在地址栏中显示出来。

由于GET方式直接利用URL传递,受限于URL的长度,GET无法传递太长的数据,一般数百字节的长度还可以容纳,数千字节可能就容纳不了了。GET方式的另一个限制是无法传送文件,因为文件数据需要使用多分段的方式传送,GET方式无法支持。

HTTP设计之初,GET方法就是为了获取某个网页的数据而存在的。GET方式传递的数据更多是作为cgi生成网页的参数使用,所以也无需处理太大量的数据。

POST

和GET方式不同,POST方式传递的数据会放在HTTP Request的body部分,而不是放在header里,所以POST方式传递的数据不会显示在地址栏里,允许容纳的数据量也更大一些。在HTTP/1.0中,POST是主动提交大量数据的唯一办法;即使在HTTP/1.1中,POST也是主要的提交大量数据的方式。

POST方式传送的数据虽然不会显示在浏览器的地址栏中,但这并不意味着它比GET方式更安全。HTTP协议本身是明文传递数据的,所有的敏感信息都有可能被网络传输过程中的任一环节截获。HTTPS等加密技术才是解决安全问题的最佳选择。不过考虑到HTTPS加密解密环节的巨大开销,一般的网站也常常只在登录的地方使用HTTPS。

POST和GET方法传递的数据是可以并存的,甚至可以重名。向一个带有URL QueryString的地址发送POST请求是可以的,此时POST的表单数据通过Request Body传递,而URL中的QueryString会被解析成GET方式传递的数据。

COOKIE

HTTP协议本来是无状态的,各次请求之间没有任何关联性,但是有的时候网站开发人员的确需要维持一个用户的会话,完成一些上下文关联的操作,所以COOKIE这种东西就被发明出来了。

COOKIE是HTTP Header中的一部分,用类似GET那样的key=value的格式描述了一系列数据。COOKIE可以设置过期时间,作用的域和路径。如果当前访问的URL的域和路径与浏览器保存的COOKIE相符,COOKIE就会自动被放在Header中发送给WebServer。从某种意义上来说,使用COOKIE传递的数据往往是一些需要浏览器“带着跑”的数据,比如当前会话的ID、用户的唯一表示符等等。

COOKIE的安全性也不比GET或者POST更高,毕竟COOKIE也是明文传送的数据,而且COOKIE往往更容易让网站陷入危机。许多WebServer端开发人员会很小心的处理GET和POST传递来的数据,但是对COOKIE却直接使用,这绝对是安全隐患。

在PHP中,会话函数(session)就是使用一个名为PHPSESSID的COOKIE实现的客户端关系维持。Session和COOKIE不同,它的所有都保存在Server端,只留下一个Session ID交给浏览器保存,SessionID的生成规则毫无规律,这样可以最大限度的保证会话数据不被外界篡改。

常见使用场景

GET常常会用在链接中,POST常常会用在表单上,COOKIE则用来维持会话。

有一个比较特别的例子是多条件组合查询表单,这个表单的查询结果往往会很多,需要分页。如果使用GET方式传递数据,创建分页链接的时候会比较简单。

Continue Reading