learn.lianglianglee.com/专栏/透视HTTP协议/18 四通八达:HTTP的重定向和跳转.md.html
2022-05-11 19:04:14 +08:00

709 lines
28 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<!-- saved from url=(0046)https://kaiiiz.github.io/hexo-theme-book-demo/ -->
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
<link rel="icon" href="/static/favicon.png">
<title>18 四通八达HTTP的重定向和跳转.md.html</title>
<!-- Spectre.css framework -->
<link rel="stylesheet" href="/static/index.css">
<!-- theme css & js -->
<meta name="generator" content="Hexo 4.2.0">
</head>
<body>
<div class="book-container">
<div class="book-sidebar">
<div class="book-brand">
<a href="/">
<img src="/static/favicon.png">
<span>技术文章摘抄</span>
</a>
</div>
<div class="book-menu uncollapsible">
<ul class="uncollapsible">
<li><a href="/" class="current-tab">首页</a></li>
</ul>
<ul class="uncollapsible">
<li><a href="../">上一级</a></li>
</ul>
<ul class="uncollapsible">
<li>
<a href="/专栏/透视HTTP协议/00 开篇词To Be a HTTP Hero.md.html">00 开篇词To Be a HTTP Hero.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/01 时势与英雄HTTP的前世今生.md.html">01 时势与英雄HTTP的前世今生.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/02 HTTP是什么HTTP又不是什么.md.html">02 HTTP是什么HTTP又不是什么.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/03 HTTP世界全览与HTTP相关的各种概念.md.html">03 HTTP世界全览与HTTP相关的各种概念.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/04 HTTP世界全览与HTTP相关的各种协议.md.html">04 HTTP世界全览与HTTP相关的各种协议.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/05 常说的“四层”和“七层”到底是什么?“五层”“六层”哪去了?.md.html">05 常说的“四层”和“七层”到底是什么?“五层”“六层”哪去了?.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/06 域名里有哪些门道?.md.html">06 域名里有哪些门道?.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/07 自己动手搭建HTTP实验环境.md.html">07 自己动手搭建HTTP实验环境.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/08 键入网址再按下回车,后面究竟发生了什么?.md.html">08 键入网址再按下回车,后面究竟发生了什么?.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/09 HTTP报文是什么样子的.md.html">09 HTTP报文是什么样子的.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/10 应该如何理解请求方法?.md.html">10 应该如何理解请求方法?.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/11 你能写出正确的网址吗?.md.html">11 你能写出正确的网址吗?.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/12 响应状态码该怎么用?.md.html">12 响应状态码该怎么用?.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/13 HTTP有哪些特点.md.html">13 HTTP有哪些特点.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/14 HTTP有哪些优点又有哪些缺点.md.html">14 HTTP有哪些优点又有哪些缺点.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/15 海纳百川HTTP的实体数据.md.html">15 海纳百川HTTP的实体数据.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/16 把大象装进冰箱HTTP传输大文件的方法.md.html">16 把大象装进冰箱HTTP传输大文件的方法.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/17 排队也要讲效率HTTP的连接管理.md.html">17 排队也要讲效率HTTP的连接管理.md.html</a>
</li>
<li>
<a class="current-tab" href="/专栏/透视HTTP协议/18 四通八达HTTP的重定向和跳转.md.html">18 四通八达HTTP的重定向和跳转.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/19 让我知道你是谁HTTP的Cookie机制.md.html">19 让我知道你是谁HTTP的Cookie机制.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/20 生鲜速递HTTP的缓存控制.md.html">20 生鲜速递HTTP的缓存控制.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/21 良心中间商HTTP的代理服务.md.html">21 良心中间商HTTP的代理服务.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/22 冷链周转HTTP的缓存代理.md.html">22 冷链周转HTTP的缓存代理.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/23 HTTPS是什么SSLTLS又是什么.md.html">23 HTTPS是什么SSLTLS又是什么.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/24 固若金汤的根本(上):对称加密与非对称加密.md.html">24 固若金汤的根本(上):对称加密与非对称加密.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/25 固若金汤的根本(下):数字签名与证书.md.html">25 固若金汤的根本(下):数字签名与证书.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/26 信任始于握手TLS1.2连接过程解析.md.html">26 信任始于握手TLS1.2连接过程解析.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/27 更好更快的握手TLS1.3特性解析.md.html">27 更好更快的握手TLS1.3特性解析.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/28 连接太慢该怎么办HTTPS的优化.md.html">28 连接太慢该怎么办HTTPS的优化.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/29 我应该迁移到HTTPS吗.md.html">29 我应该迁移到HTTPS吗.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/30 时代之风HTTP2特性概览.md.html">30 时代之风HTTP2特性概览.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/31 时代之风HTTP2内核剖析.md.html">31 时代之风HTTP2内核剖析.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/32 未来之路HTTP3展望.md.html">32 未来之路HTTP3展望.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/33 我应该迁移到HTTP2吗.md.html">33 我应该迁移到HTTP2吗.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/34 Nginx高性能的Web服务器.md.html">34 Nginx高性能的Web服务器.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/35 OpenResty更灵活的Web服务器.md.html">35 OpenResty更灵活的Web服务器.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/36 WAF保护我们的网络服务.md.html">36 WAF保护我们的网络服务.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/37 CDN加速我们的网络服务.md.html">37 CDN加速我们的网络服务.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/38 WebSocket沙盒里的TCP.md.html">38 WebSocket沙盒里的TCP.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/39 HTTP性能优化面面观.md.html">39 HTTP性能优化面面观.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/40 HTTP性能优化面面观.md.html">40 HTTP性能优化面面观.md.html</a>
</li>
<li>
<a href="/专栏/透视HTTP协议/结束语 做兴趣使然的Hero.md.html">结束语 做兴趣使然的Hero.md.html</a>
</li>
</ul>
</div>
</div>
<div class="sidebar-toggle" onclick="sidebar_toggle()" onmouseover="add_inner()" onmouseleave="remove_inner()">
<div class="sidebar-toggle-inner"></div>
</div>
<script>
function add_inner() {
let inner = document.querySelector('.sidebar-toggle-inner')
inner.classList.add('show')
}
function remove_inner() {
let inner = document.querySelector('.sidebar-toggle-inner')
inner.classList.remove('show')
}
function sidebar_toggle() {
let sidebar_toggle = document.querySelector('.sidebar-toggle')
let sidebar = document.querySelector('.book-sidebar')
let content = document.querySelector('.off-canvas-content')
if (sidebar_toggle.classList.contains('extend')) { // show
sidebar_toggle.classList.remove('extend')
sidebar.classList.remove('hide')
content.classList.remove('extend')
} else { // hide
sidebar_toggle.classList.add('extend')
sidebar.classList.add('hide')
content.classList.add('extend')
}
}
function open_sidebar() {
let sidebar = document.querySelector('.book-sidebar')
let overlay = document.querySelector('.off-canvas-overlay')
sidebar.classList.add('show')
overlay.classList.add('show')
}
function hide_canvas() {
let sidebar = document.querySelector('.book-sidebar')
let overlay = document.querySelector('.off-canvas-overlay')
sidebar.classList.remove('show')
overlay.classList.remove('show')
}
</script>
<div class="off-canvas-content">
<div class="columns">
<div class="column col-12 col-lg-12">
<div class="book-navbar">
<!-- For Responsive Layout -->
<header class="navbar">
<section class="navbar-section">
<a onclick="open_sidebar()">
<i class="icon icon-menu"></i>
</a>
</section>
</header>
</div>
<div class="book-content" style="max-width: 960px; margin: 0 auto;
overflow-x: auto;
overflow-y: hidden;">
<div class="book-post">
<p id="tip" align="center"></p>
<div><h1>18 四通八达HTTP的重定向和跳转</h1>
<p>在专栏[第 1 讲]时我曾经说过,为了实现在互联网上构建超链接文档系统的设想,蒂姆·伯纳斯 - 李发明了万维网,使用 HTTP 协议传输“超文本”,让全世界的人都能够自由地共享信息。</p>
<p>“超文本”里含有“超链接”,可以从一个“超文本”跳跃到另一个“超文本”,对线性结构的传统文档是一个根本性的变革。</p>
<p>能够使用“超链接”在网络上任意地跳转也是万维网的一个关键特性。它把分散在世界各地的文档连接在一起,形成了复杂的网状结构,用户可以在查看时随意点击链接、转换页面。再加上浏览器又提供了“前进”“后退”“书签”等辅助功能,让用户在文档间跳转时更加方便,有了更多的主动性和交互性。</p>
<p>那么,点击页面“链接”时的跳转是怎样的呢?具体一点,比如在 Nginx 的主页上点了一下“download”链接会发生什么呢</p>
<p>结合之前的课程,稍微思考一下你就能得到答案:浏览器首先要解析链接文字里的 URI。</p>
<pre><code>http://nginx.org/en/download.html
</code></pre>
<p>再用这个 URI 发起一个新的 HTTP 请求,获取响应报文后就会切换显示内容,渲染出新 URI 指向的页面。</p>
<p>这样的跳转动作是由浏览器的使用者主动发起的,可以称为“<strong>主动跳转</strong>”,但还有一类跳转是由服务器来发起的,浏览器使用者无法控制,相对地就可以称为“<strong>被动跳转</strong>”,这在 HTTP 协议里有个专门的名词,叫做“<strong>重定向</strong>Redirection</p>
<h2>重定向的过程</h2>
<p>其实之前我们就已经见过重定向了,在[第 12 讲]里 3××状态码时就说过301 是“永久重定向”302 是“临时重定向”,浏览器收到这两个状态码就会跳转到新的 URI。</p>
<p>那么,它们是怎么做到的呢?难道仅仅用这两个代码就能够实现跳转页面吗?</p>
<p>先在实验环境里看一下重定向的过程吧,用 Chrome 访问 URI “/18-1”它会使用 302 立即跳转到“/index.html”。</p>
<p>从这个实验可以看到,这一次“重定向”实际上发送了两次 HTTP 请求,第一个请求返回了 302然后第二个请求就被重定向到了“/index.html”。但如果不用开发者工具的话你是完全看不到这个跳转过程的也就是说重定向是“用户无感知”的。</p>
<p>我们再来看看第一个请求返回的响应报文:</p>
<p><img src="assets/a4276db758bf90f63fd5a5c2357af4ac.png" alt="img" /></p>
<p>这里出现了一个新的头字段“Location: /index.html”它就是 301/302 重定向跳转的秘密所在。</p>
<p><strong>Location</strong>”字段属于响应字段,必须出现在响应报文里。但只有配合 301/302 状态码才有意义,它<strong>标记了服务器要求重定向的 URI</strong>这里就是要求浏览器跳转到“index.html”。</p>
<p>浏览器收到 301/302 报文会检查响应头里有没有“Location”。如果有就从字段值里提取出 URI发出新的 HTTP 请求,相当于自动替我们点击了这个链接。</p>
<p>在“Location”里的 URI 既可以使用绝对 URI也可以使用相对 URI。所谓“绝对 URI”就是完整形式的 URI包括 scheme、host:port、path 等。所谓“相对 URI”就是省略了 scheme 和 host:port只有 path 和 query 部分,是不完整的,但可以从请求上下文里计算得到。</p>
<p>例如刚才的实验例子里的“Location: /index.html”用的就是相对 URI。它没有说明访问 URI 的协议和主机,但因为是由“<a href="http://www.chrono.com/18-1">http://www.chrono.com/18-1</a>”重定向返回的响应报文,所以浏览器就可以拼出完整的 URI</p>
<pre><code>http://www.chrono.com/index.html
</code></pre>
<p>实验环境的 URI“/18-1”还支持使用 query 参数“dst=xxx”指明重定向的 URI你可以用这种形式再多试几次重定向看看浏览器是如何工作的。</p>
<pre><code>http://www.chrono.com/18-1?dst=/15-1?name=a.json
http://www.chrono.com/18-1?dst=/17-1
</code></pre>
<p>注意,在重定向时如果只是在站内跳转,你可以放心地使用相对 URI。但如果要跳转到站外就必须用绝对 URI。</p>
<p>例如,如果想跳转到 Nginx 官网就必须在“nginx.org”前把“http://”都写出来,否则浏览器会按照相对 URI 去理解,得到的就会是一个不存在的 URI“<a href="http://www.chrono.com/nginx.org”">http://www.chrono.com/nginx.org”</a></p>
<pre><code>http://www.chrono.com/18-1?dst=nginx.org # 错误
http://www.chrono.com/18-1?dst=http://nginx.org # 正确
</code></pre>
<p><img src="assets/006059602ee75b176a80429f49ffc9aa.png" alt="img" /></p>
<p>那么,如果 301/302 跳转时没有 Location 字段会怎么样呢?</p>
<p>这个你也可以自己试一下,使用第 12 讲里的 URI“/12-1”查询参数用“code=302”</p>
<pre><code>http://www.chrono.com/12-1?code=302
</code></pre>
<h2>重定向状态码</h2>
<p>刚才我把重定向的过程基本讲完了,现在来说一下重定向用到的状态码。</p>
<p>最常见的重定向状态码就是 301 和 302另外还有几个不太常见的例如 303、307、308 等。它们最终的效果都差不多,让浏览器跳转到新的 URI但语义上有一些细微的差别使用的时候要特别注意。</p>
<p><strong>301</strong>俗称“永久重定向”Moved Permanently意思是原 URI 已经“永久”性地不存在了,今后的所有请求都必须改用新的 URI。</p>
<p>浏览器看到 301就知道原来的 URI“过时”了就会做适当的优化。比如历史记录、更新书签下次可能就会直接用新的 URI 访问,省去了再次跳转的成本。搜索引擎的爬虫看到 301也会更新索引库不再使用老的 URI。</p>
<p><strong>302</strong>俗称“临时重定向”“Moved Temporarily”意思是原 URI 处于“临时维护”状态,新的 URI 是起“顶包”作用的“临时工”。</p>
<p>浏览器或者爬虫看到 302会认为原来的 URI 仍然有效,但暂时不可用,所以只会执行简单的跳转页面,不记录新的 URI也不会有其他的多余动作下次访问还是用原 URI。</p>
<p>301/302 是最常用的重定向状态码,在 3××里剩下的几个还有</p>
<ul>
<li>303 See Other类似 302但要求重定向后的请求改为 GET 方法,访问一个结果页面,避免 POST/PUT 重复操作;</li>
<li>307 Temporary Redirect类似 302但重定向后请求里的方法和实体不允许变动含义比 302 更明确;</li>
<li>308 Permanent Redirect类似 307不允许重定向后的请求变动但它是 301“永久重定向”的含义。</li>
</ul>
<p>不过这三个状态码的接受程度较低,有的浏览器和服务器可能不支持,开发时应当慎重,测试确认浏览器的实际效果后才能使用。</p>
<h2>重定向的应用场景</h2>
<p>理解了重定向的工作原理和状态码的含义,我们就可以<strong>在服务器端拥有主动权</strong>,控制浏览器的行为,不过要怎么利用重定向才好呢?</p>
<p>使用重定向跳转,核心是要理解“<strong>重定向</strong>”和“<strong>永久 / 临时</strong>”这两个关键词。</p>
<p>先来看什么时候需要重定向。</p>
<p>一个最常见的原因就是“<strong>资源不可用</strong>”,需要用另一个新的 URI 来代替。</p>
<p>至于不可用的原因那就很多了。例如域名变更、服务器变更、网站改版、系统维护,这些都会导致原 URI 指向的资源无法访问,为了避免出现 404就需要用重定向跳转到新的 URI继续为网民提供服务。</p>
<p>另一个原因就是“<strong>避免重复</strong>”,让多个网址都跳转到一个 URI增加访问入口的同时还不会增加额外的工作量。</p>
<p>例如有的网站都会申请多个名称类似的域名然后把它们再重定向到主站上。比如你可以访问一下“qq.com”“github.com ”“bing.com”记得事先清理缓存看看它是如何重定向的。</p>
<p>决定要实行重定向后接下来要考虑的就是“永久”和“临时”的问题了,也就是选择 301 还是 302。</p>
<p>301 的含义是“<strong>永久</strong>”的。</p>
<p>如果域名、服务器、网站架构发生了大幅度的改变,比如启用了新域名、服务器切换到了新机房、网站目录层次重构,这些都算是“永久性”的改变。原来的 URI 已经不能用了,必须用 301“永久重定向”通知浏览器和搜索引擎更新到新地址这也是搜索引擎优化SEO要考虑的因素之一。</p>
<p>302 的含义是“<strong>临时</strong>”的。</p>
<p>原来的 URI 在将来的某个时间点还会恢复正常,常见的应用场景就是系统维护,把网站重定向到一个通知页面,告诉用户过一会儿再来访问。另一种用法就是“服务降级”,比如在双十一促销的时候,把订单查询、领积分等不重要的功能入口暂时关闭,保证核心服务能够正常运行。</p>
<h2>重定向的相关问题</h2>
<p>重定向的用途很多,掌握了重定向,就能够在架设网站时获得更多的灵活性,不过在使用时还需要注意两个问题。</p>
<p>第一个问题是“<strong>性能损耗</strong>”。很明显,重定向的机制决定了一个跳转会有两次请求 - 应答,比正常的访问多了一次。</p>
<p>虽然 301/302 报文很小,但大量的跳转对服务器的影响也是不可忽视的。站内重定向还好说,可以长连接复用,站外重定向就要开两个连接,如果网络连接质量差,那成本可就高多了,会严重影响用户的体验。</p>
<p>所以重定向应当适度使用,决不能滥用。</p>
<p>第二个问题是“<strong>循环跳转</strong>”。如果重定向的策略设置欠考虑可能会出现“A=&gt;B=&gt;C=&gt;A”的无限循环不停地在这个链路里转圈圈后果可想而知。</p>
<p>所以 HTTP 协议特别规定,浏览器必须具有检测“循环跳转”的能力,在发现这种情况时应当停止发送请求并给出错误提示。</p>
<p>实验环境的 URI“/18-2”就模拟了这样的一个“循环跳转”它跳转到“/18-1”并用参数“dst=/18-2”再跳回自己实现了两个 URI 的无限循环。</p>
<p>使用 Chrome 访问这个地址,会得到“该网页无法正常运作”的结果:</p>
<p><img src="assets/4b91aeea08d90f173c62493934e5f52f.png" alt="img" /></p>
<h2>小结</h2>
<p>今天我们学习了 HTTP 里的重定向和跳转,简单小结一下这次的内容:</p>
<ol>
<li>重定向是服务器发起的跳转,要求客户端改用新的 URI 重新发送请求,通常会自动进行,用户是无感知的;</li>
<li>301/302 是最常用的重定向状态码,分别是“永久重定向”和“临时重定向”;</li>
<li>响应头字段 Location 指示了要跳转的 URI可以用绝对或相对的形式</li>
<li>重定向可以把一个 URI 指向另一个 URI也可以把多个 URI 指向同一个 URI用途很多</li>
<li>使用重定向时需要当心性能损耗,还要避免出现循环跳转。</li>
</ol>
<h2>课下作业</h2>
<ol>
<li>301 和 302 非常相似,试着结合第 12 讲,用自己的理解再描述一下两者的异同点。</li>
<li>你能结合自己的实际情况,再列出几个应当使用重定向的场景吗?</li>
</ol>
<p>欢迎你把自己的学习体会写在留言区,与我和其他同学一起讨论。如果你觉得有所收获,也欢迎把文章分享给你的朋友。</p>
<p><img src="assets/9b873d25e33f86bb2818fc8b50fbff05.png" alt="unpreview" /></p>
</div>
</div>
<div>
<div style="float: left">
<a href="/专栏/透视HTTP协议/17 排队也要讲效率HTTP的连接管理.md.html">上一页</a>
</div>
<div style="float: right">
<a href="/专栏/透视HTTP协议/19 让我知道你是谁HTTP的Cookie机制.md.html">下一页</a>
</div>
</div>
</div>
</div>
</div>
</div>
<a class="off-canvas-overlay" onclick="hide_canvas()"></a>
</div>
<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v652eace1692a40cfa3763df669d7439c1639079717194" integrity="sha512-Gi7xpJR8tSkrpF7aordPZQlW2DLtzUlZcumS8dMQjwDHEnw9I7ZLyiOj/6tZStRBGtGgN6ceN6cMH8z7etPGlw==" data-cf-beacon='{"rayId":"70997d065e293cfa","version":"2021.12.0","r":1,"token":"1f5d475227ce4f0089a7cff1ab17c0f5","si":100}' crossorigin="anonymous"></script>
</body>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-NPSEEVD756"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'G-NPSEEVD756');
var path = window.location.pathname
var cookie = getCookie("lastPath");
console.log(path)
if (path.replace("/", "") === "") {
if (cookie.replace("/", "") !== "") {
console.log(cookie)
document.getElementById("tip").innerHTML = "<a href='" + cookie + "'>跳转到上次进度</a>"
}
} else {
setCookie("lastPath", path)
}
function setCookie(cname, cvalue) {
var d = new Date();
d.setTime(d.getTime() + (180 * 24 * 60 * 60 * 1000));
var expires = "expires=" + d.toGMTString();
document.cookie = cname + "=" + cvalue + "; " + expires + ";path = /";
}
function getCookie(cname) {
var name = cname + "=";
var ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i++) {
var c = ca[i].trim();
if (c.indexOf(name) === 0) return c.substring(name.length, c.length);
}
return "";
}
</script>
</html>