pin_drop当前位置:知识文库 ❯ 图文

后端通信:HTTP头部详解 - 从入门到实践详解

教程简介

HTTP头部(Header)是HTTP报文中不可或缺的组成部分,它承载了请求和响应的元数据信息。从内容协商(Content-Type、Accept)到缓存控制(Cache-Control、ETag),从认证授权(Authorization、Cookie)到跨域通信(CORS相关头部),HTTP头部几乎控制着Web通信的方方面面。深入理解HTTP头部的工作机制,是掌握前后端通信、优化应用性能、保障安全的关键。

本教程将全面讲解HTTP请求头、响应头、内容类型头部、认证头部、缓存头部、CORS头部、Cookie头部以及自定义头部的使用方法和最佳实践。

核心概念

1. HTTP头部概述

HTTP头部是键值对形式出现的元数据,位于请求行/状态行之后、请求体/响应体之前,以空行分隔。

格式规范:

  • 每行一个头部字段:Name: Value

  • 头部名称不区分大小写

  • 冒号后可以有空格也可以没有

  • 空行表示头部结束

2. 头部分类

分类 说明 示例
请求头 客户端发送的头部 User-Agent、Accept、Authorization
响应头 服务器返回的头部 Server、Set-Cookie、Content-Type
表示头 描述消息体的元数据 Content-Type、Content-Length、Content-Encoding
条件头 设置请求条件 If-None-Match、If-Modified-Since
缓存头 控制缓存行为 Cache-Control、ETag、Expires
认证头 身份验证信息 Authorization、WWW-Authenticate
CORS头 跨域资源共享 Access-Control-Allow-Origin

3. 通用头部详解

Content-Type

指定请求体或响应体的媒体类型(MIME类型):

说明 使用场景
application/json JSON格式 API请求/响应
application/x-www-form-urlencoded URL编码表单 普通表单提交
multipart/form-data 多部分表单 文件上传
text/html HTML文档 网页响应
text/plain 纯文本 简单文本
text/css CSS样式 样式文件
application/javascript JavaScript 脚本文件
image/png PNG图片 图片响应
application/octet-stream 二进制流 文件下载

Accept

客户端声明能够处理的响应内容类型:

代码示例

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
  • q表示权重(quality value),范围0-1,默认1

  • */*表示接受任何类型

Authorization

携带认证凭据的请求头:

代码示例

// Basic认证
Authorization: Basic dXNlcjpwYXNzd29yZA==

// Bearer Token
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

// API Key
Authorization: ApiKey abc123def456

Cache-Control

控制缓存行为的头部,是最重要的缓存控制机制:

请求中的Cache-Control

指令 说明
no-cache 不使用缓存,必须向服务器验证
no-store 不缓存任何内容
max-age=0 缓存已过期,需要验证
max-age=3600 缓存3600秒内有效
only-if-cached 只使用缓存,不请求服务器

响应中的Cache-Control

指令 说明
public 任何缓存都可以存储
private 只有浏览器可以缓存
no-cache 可以缓存但每次必须验证
no-store 不缓存
max-age=3600 缓存3600秒有效
must-revalidate 过期后必须验证
immutable 资源不会变化,无需验证

Cookie相关头部

请求头 - Cookie:浏览器自动携带Cookie

代码示例

Cookie: sessionId=abc123; theme=dark; lang=zh-CN

响应头 - Set-Cookie:服务器设置Cookie

代码示例

Set-Cookie: sessionId=abc123; Path=/; HttpOnly; Secure; SameSite=Strict; Max-Age=3600

Cookie属性说明:

属性 说明
Path Cookie生效的路径
Domain Cookie生效的域名
HttpOnly JavaScript无法访问(防XSS)
Secure 仅HTTPS传输
SameSite 跨站发送限制(Strict/Lax/None)
Max-Age 有效期(秒)
Expires 过期时间

CORS相关头部

请求头

代码示例

Origin: https://www.example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type, Authorization

响应头

代码示例

Access-Control-Allow-Origin: https://www.example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 86400
Access-Control-Expose-Headers: X-Custom-Header

4. 其他重要头部

请求头

头部 说明 示例
Host 请求的目标主机名 Host: api.example.com
User-Agent 客户端标识 User-Agent: Mozilla/5.0...
Referer 来源页面URL Referer: https://example.com/page
Accept-Encoding 支持的压缩方式 Accept-Encoding: gzip, deflate, br
Accept-Language 偏好语言 Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Connection 连接管理 Connection: keep-alive
If-None-Match 条件请求(ETag) If-None-Match: "abc123"
If-Modified-Since 条件请求(时间) If-Modified-Since: Mon, 22 Apr 2026 00:00:00 GMT
Range 范围请求 Range: bytes=0-1023

响应头

头部 说明 示例
Server 服务器软件 Server: nginx/1.24.0
Date 响应时间 Date: Mon, 22 Apr 2026 10:30:00 GMT
Location 重定向目标 Location: https://new.example.com
ETag 资源标识 ETag: "abc123"
Last-Modified 最后修改时间 Last-Modified: Mon, 22 Apr 2026 00:00:00 GMT
Content-Length 响应体大小 Content-Length: 1024
Content-Encoding 响应体编码 Content-Encoding: gzip
X-Request-Id 请求追踪ID X-Request-Id: req-abc123
X-RateLimit-Limit 限流上限 X-RateLimit-Limit: 100
X-RateLimit-Remaining 剩余次数 X-RateLimit-Remaining: 95

5. 自定义头部

自定义头部以X-前缀开头(传统惯例,RFC 6648已废弃此惯例):

代码示例

// 请求自定义头部
X-Api-Version: 2
X-Request-ID: abc123
X-Client-Version: 1.0.0

// 响应自定义头部
X-Request-ID: abc123
X-RateLimit-Limit: 100
X-Total-Count: 500

注意:自定义头部可能触发CORS预检请求。

语法与用法

Fetch API中设置和读取头部

代码示例

// 设置请求头
const response = await fetch('/api/users', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
        'Authorization': 'Bearer token123',
        'X-Custom-Header': 'custom-value'
    },
    body: JSON.stringify({ name: '张三' })
});

// 读取响应头
const contentType = response.headers.get('Content-Type');
const requestId = response.headers.get('X-Request-Id');
const rateLimit = response.headers.get('X-RateLimit-Remaining');

// 遍历响应头
response.headers.forEach((value, name) => {
    console.log(`${name}: ${value}`);
});

// 使用Headers对象
const headers = new Headers();
headers.set('Content-Type', 'application/json');
headers.append('Accept', 'application/json');
headers.append('Accept', 'text/html');
const hasAuth = headers.has('Authorization');
headers.delete('X-Temp-Header');

代码示例

示例1:HTTP头部浏览器与编辑器

代码示例

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>HTTP头部浏览器与编辑器</title>
    <style>
        * { margin: 0; padding: 0; box-sizing: border-box; }
        body {
            font-family: 'Segoe UI', system-ui, sans-serif;
            background: #f0f2f5;
            padding: 20px;
            color: #333;
        }
        .container { max-width: 1000px; margin: 0 auto; }
        h1 { text-align: center; color: #1a73e8; margin-bottom: 24px; font-size: 28px; }
        .card {
            background: #fff;
            border-radius: 12px;
            padding: 24px;
            margin-bottom: 20px;
            box-shadow: 0 2px 8px rgba(0,0,0,0.08);
        }
        .card h2 {
            font-size: 18px;
            color: #1a73e8;
            margin-bottom: 16px;
            padding-bottom: 8px;
            border-bottom: 2px solid #e8eaed;
        }
        .header-category {
            margin-bottom: 20px;
        }
        .category-title {
            font-size: 15px;
            font-weight: 700;
            padding: 8px 14px;
            border-radius: 6px;
            margin-bottom: 10px;
            display: flex;
            align-items: center;
            justify-content: space-between;
        }
        .cat-content { background: #e8f5e9; color: #2e7d32; }
        .cat-auth { background: #fce4ec; color: #c62828; }
        .cat-cache { background: #fff3e0; color: #ef6c00; }
        .cat-cors { background: #e3f2fd; color: #1565c0; }
        .cat-cookie { background: #f3e5f5; color: #7b1fa2; }
        .cat-custom { background: #e0f2f1; color: #00695c; }
        .header-item {
            display: flex;
            align-items: flex-start;
            padding: 12px;
            border-bottom: 1px solid #f0f0f0;
            gap: 12px;
        }
        .header-item:last-child { border-bottom: none; }
        .header-name {
            font-family: 'Consolas', monospace;
            font-weight: 700;
            font-size: 14px;
            min-width: 200px;
            color: #1a73e8;
        }
        .header-value {
            font-family: 'Consolas', monospace;
            font-size: 13px;
            color: #555;
            flex: 1;
        }
        .header-desc {
            font-size: 12px;
            color: #999;
            margin-top: 4px;
        }
        .tag {
            display: inline-block;
            padding: 2px 8px;
            border-radius: 4px;
            font-size: 11px;
            font-weight: 600;
        }
        .tag-request { background: #e3f2fd; color: #1565c0; }
        .tag-response { background: #e8f5e9; color: #2e7d32; }
        .tag-both { background: #fff3e0; color: #ef6c00; }
        .filter-bar {
            display: flex;
            gap: 8px;
            margin-bottom: 16px;
            flex-wrap: wrap;
        }
        .filter-btn {
            padding: 6px 14px;
            border: 1px solid #dadce0;
            border-radius: 20px;
            background: #fff;
            cursor: pointer;
            font-size: 13px;
            transition: all 0.2s;
        }
        .filter-btn.active { background: #1a73e8; color: #fff; border-color: #1a73e8; }
        .search-input {
            width: 100%;
            padding: 10px 16px;
            border: 1px solid #dadce0;
            border-radius: 8px;
            font-size: 14px;
            margin-bottom: 16px;
        }
        .search-input:focus { outline: none; border-color: #1a73e8; }
    </style>
</head>
<body>
    <div class="container">
        <h1>HTTP头部浏览器与编辑器</h1>

        <div class="card">
            <h2>搜索与筛选</h2>
            <input type="text" class="search-input" id="searchInput" placeholder="搜索头部名称或描述...">
            <div class="filter-bar">
                <div class="filter-btn active" onclick="filterBy('all')">全部</div>
                <div class="filter-btn" onclick="filterBy('request')">请求头</div>
                <div class="filter-btn" onclick="filterBy('response')">响应头</div>
                <div class="filter-btn" onclick="filterBy('content')">内容类型</div>
                <div class="filter-btn" onclick="filterBy('auth')">认证</div>
                <div class="filter-btn" onclick="filterBy('cache')">缓存</div>
                <div class="filter-btn" onclick="filterBy('cors')">CORS</div>
            </div>
        </div>

        <div id="headerList"></div>
    </div>

    <script>
        const headers = [
            { name: 'Content-Type', type: 'both', category: 'content', value: 'application/json', desc: '指定请求体或响应体的MIME类型。API常用application/json,表单用application/x-www-form-urlencoded,文件上传用multipart/form-data。' },
            { name: 'Content-Length', type: 'both', category: 'content', value: '1024', desc: '请求体或响应体的大小(字节)。浏览器和服务器通常自动设置。' },
            { name: 'Content-Encoding', type: 'response', category: 'content', value: 'gzip', desc: '响应体使用的编码方式,如gzip、deflate、br(Brotli)。用于压缩传输减少带宽。' },
            { name: 'Content-Disposition', type: 'response', category: 'content', value: 'attachment; filename="file.pdf"', desc: '指示响应内容如何显示。attachment表示下载,inline表示内联显示。' },
            { name: 'Accept', type: 'request', category: 'content', value: 'application/json, text/html;q=0.9', desc: '客户端声明能够处理的响应内容类型。q值表示优先级权重(0-1)。' },
            { name: 'Accept-Encoding', type: 'request', category: 'content', value: 'gzip, deflate, br', desc: '客户端支持的压缩编码方式。服务器据此选择合适的压缩算法。' },
            { name: 'Accept-Language', type: 'request', category: 'content', value: 'zh-CN,zh;q=0.9,en;q=0.8', desc: '客户端偏好的自然语言。服务器据此返回对应语言的内容。' },
            { name: 'Authorization', type: 'request', category: 'auth', value: 'Bearer eyJhbGci...', desc: '携带认证凭据。常用格式:Basic(Base64编码的用户名密码)、Bearer(JWT令牌)。' },
            { name: 'WWW-Authenticate', type: 'response', category: 'auth', value: 'Bearer realm="api"', desc: '401响应中指示客户端应使用的认证方式。' },
            { name: 'Cookie', type: 'request', category: 'cookie', value: 'sessionId=abc123; theme=dark', desc: '浏览器自动携带的Cookie值。包含该域名下所有未过期的Cookie。' },
            { name: 'Set-Cookie', type: 'response', category: 'cookie', value: 'sessionId=abc123; HttpOnly; Secure; SameSite=Strict', desc: '服务器设置Cookie。可指定Path、Domain、HttpOnly、Secure、SameSite等属性。' },
            { name: 'Cache-Control', type: 'both', category: 'cache', value: 'max-age=3600, public', desc: '最重要的缓存控制头部。请求中可设no-cache/no-store,响应中可设max-age/public/private等。' },
            { name: 'ETag', type: 'response', category: 'cache', value: '"abc123def456"', desc: '资源的唯一标识符。客户端下次请求时通过If-None-Match携带,服务器据此判断资源是否变化。' },
            { name: 'If-None-Match', type: 'request', category: 'cache', value: '"abc123def456"', desc: '条件请求头部。携带上次响应的ETag值,服务器比对后返回304或新资源。' },
            { name: 'Last-Modified', type: 'response', category: 'cache', value: 'Mon, 22 Apr 2026 00:00:00 GMT', desc: '资源最后修改时间。配合If-Modified-Since使用。' },
            { name: 'If-Modified-Since', type: 'request', category: 'cache', value: 'Mon, 22 Apr 2026 00:00:00 GMT', desc: '条件请求头部。携带上次响应的Last-Modified值。' },
            { name: 'Expires', type: 'response', category: 'cache', value: 'Mon, 22 Apr 2026 12:00:00 GMT', desc: '缓存过期时间。被Cache-Control: max-age优先级覆盖。HTTP/1.0遗留头部。' },
            { name: 'Access-Control-Allow-Origin', type: 'response', category: 'cors', value: 'https://www.example.com', desc: 'CORS核心头部。指定允许访问资源的源。*表示允许任何源,但不允许携带凭据。' },
            { name: 'Access-Control-Allow-Methods', type: 'response', category: 'cors', value: 'GET, POST, PUT, DELETE', desc: '预检响应中声明允许的HTTP方法。' },
            { name: 'Access-Control-Allow-Headers', type: 'response', category: 'cors', value: 'Content-Type, Authorization', desc: '预检响应中声明允许的请求头部。' },
            { name: 'Access-Control-Allow-Credentials', type: 'response', category: 'cors', value: 'true', desc: '指示浏览器是否暴露响应给前端JavaScript。设为true时Allow-Origin不能为*。' },
            { name: 'Access-Control-Max-Age', type: 'response', category: 'cors', value: '86400', desc: '预检请求结果的缓存时间(秒)。减少OPTIONS请求次数。' },
            { name: 'Origin', type: 'request', category: 'cors', value: 'https://www.example.com', desc: '标识请求来源的源(协议+域名+端口)。CORS和同源策略依赖此头部。' },
            { name: 'Host', type: 'request', category: 'content', value: 'api.example.com:8080', desc: '请求的目标主机名和端口。HTTP/1.1唯一必需的请求头部,支持虚拟主机。' },
            { name: 'User-Agent', type: 'request', category: 'content', value: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)...', desc: '客户端标识字符串。包含浏览器名称、版本、操作系统等信息。' },
            { name: 'Referer', type: 'request', category: 'content', value: 'https://example.com/page', desc: '请求来源页面的URL。用于统计分析和防盗链。注意拼写是Referer而非Referrer。' },
            { name: 'Location', type: 'response', category: 'content', value: 'https://new.example.com/page', desc: '重定向目标URL。配合3xx状态码使用,201响应中指向新创建的资源。' },
            { name: 'X-Request-ID', type: 'both', category: 'custom', value: 'req-abc123def456', desc: '请求追踪ID。用于分布式系统中追踪请求链路,便于问题排查。' },
            { name: 'X-RateLimit-Limit', type: 'response', category: 'custom', value: '100', desc: 'API限流上限。告知客户端当前时间窗口内允许的最大请求数。' },
            { name: 'X-RateLimit-Remaining', type: 'response', category: 'custom', value: '95', desc: '剩余请求次数。客户端可据此调整请求频率。' },
        ];

        let currentFilter = 'all';

        function renderHeaders(search = '') {
            const container = document.getElementById('headerList');
            const lowerSearch = search.toLowerCase();

            const filtered = headers.filter(h => {
                const matchFilter = currentFilter === 'all' ||
                    h.type === currentFilter ||
                    h.category === currentFilter;
                const matchSearch = !search ||
                    h.name.toLowerCase().includes(lowerSearch) ||
                    h.desc.includes(search);
                return matchFilter && matchSearch;
            });

            const categories = {};
            filtered.forEach(h => {
                if (!categories[h.category]) categories[h.category] = [];
                categories[h.category].push(h);
            });

            const catNames = {
                content: { name: '内容类型头部', css: 'cat-content' },
                auth: { name: '认证授权头部', css: 'cat-auth' },
                cache: { name: '缓存控制头部', css: 'cat-cache' },
                cors: { name: 'CORS跨域头部', css: 'cat-cors' },
                cookie: { name: 'Cookie相关头部', css: 'cat-cookie' },
                custom: { name: '自定义头部', css: 'cat-custom' }
            };

            container.innerHTML = '';
            for (const [cat, items] of Object.entries(categories)) {
                const catInfo = catNames[cat] || { name: cat, css: '' };
                container.innerHTML += `
                    <div class="card">
                        <div class="category-title ${catInfo.css}">${catInfo.name}</div>
                        ${items.map(h => `
                            <div class="header-item">
                                <div>
                                    <div class="header-name">${h.name}</div>
                                    <span class="tag tag-${h.type === 'both' ? 'both' : h.type}">${h.type === 'both' ? '请求/响应' : h.type === 'request' ? '请求头' : '响应头'}</span>
                                </div>
                                <div style="flex:1;">
                                    <div class="header-value">${h.value}</div>
                                    <div class="header-desc">${h.desc}</div>
                                </div>
                            </div>
                        `).join('')}
                    </div>
                `;
            }
        }

        function filterBy(type) {
            currentFilter = type;
            document.querySelectorAll('.filter-btn').forEach(b => b.classList.remove('active'));
            event.currentTarget.classList.add('active');
            renderHeaders(document.getElementById('searchInput').value);
        }

        document.getElementById('searchInput').addEventListener('input', (e) => {
            renderHeaders(e.target.value);
        });

        renderHeaders();
    </script>
</body>
</html>

示例2:请求头配置与响应头查看器

代码示例

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>请求头配置与响应头查看器</title>
    <style>
        * { margin: 0; padding: 0; box-sizing: border-box; }
        body {
            font-family: 'Segoe UI', system-ui, sans-serif;
            background: #f0f2f5;
            padding: 20px;
            color: #333;
        }
        .container { max-width: 900px; margin: 0 auto; }
        h1 { text-align: center; color: #1a73e8; margin-bottom: 24px; font-size: 28px; }
        .card {
            background: #fff;
            border-radius: 12px;
            padding: 24px;
            margin-bottom: 20px;
            box-shadow: 0 2px 8px rgba(0,0,0,0.08);
        }
        .card h2 {
            font-size: 18px;
            color: #1a73e8;
            margin-bottom: 16px;
            padding-bottom: 8px;
            border-bottom: 2px solid #e8eaed;
        }
        .header-row {
            display: flex;
            gap: 8px;
            margin-bottom: 8px;
            align-items: center;
        }
        .header-row input, .header-row select {
            padding: 8px 12px;
            border: 1px solid #dadce0;
            border-radius: 6px;
            font-size: 14px;
        }
        .header-row .name-input { width: 200px; font-family: 'Consolas', monospace; }
        .header-row .value-input { flex: 1; font-family: 'Consolas', monospace; }
        .remove-btn {
            padding: 6px 12px;
            background: #ea4335;
            color: #fff;
            border: none;
            border-radius: 6px;
            cursor: pointer;
            font-size: 13px;
        }
        .add-btn {
            padding: 8px 16px;
            background: #34a853;
            color: #fff;
            border: none;
            border-radius: 6px;
            cursor: pointer;
            font-size: 13px;
            margin-top: 8px;
        }
        .preset-btns {
            display: flex;
            gap: 8px;
            margin-bottom: 16px;
            flex-wrap: wrap;
        }
        .preset-btn {
            padding: 6px 14px;
            border: 1px solid #dadce0;
            border-radius: 6px;
            background: #fff;
            cursor: pointer;
            font-size: 13px;
            transition: all 0.2s;
        }
        .preset-btn:hover { border-color: #1a73e8; color: #1a73e8; }
        .send-btn {
            padding: 12px 32px;
            background: #1a73e8;
            color: #fff;
            border: none;
            border-radius: 8px;
            font-size: 16px;
            font-weight: 700;
            cursor: pointer;
            transition: all 0.2s;
        }
        .send-btn:hover { background: #1557b0; }
        .url-input {
            width: 100%;
            padding: 12px 16px;
            border: 1px solid #dadce0;
            border-radius: 8px;
            font-size: 15px;
            margin-bottom: 16px;
        }
        .url-input:focus { outline: none; border-color: #1a73e8; }
        .response-headers {
            background: #1e1e1e;
            color: #d4d4d4;
            padding: 16px;
            border-radius: 8px;
            font-family: 'Consolas', monospace;
            font-size: 13px;
            line-height: 1.8;
            white-space: pre-wrap;
        }
        .header-highlight { color: #569cd6; }
        .value-highlight { color: #ce9178; }
        .info-note {
            padding: 12px;
            background: #e3f2fd;
            border-radius: 6px;
            font-size: 13px;
            color: #1565c0;
            margin-top: 12px;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>请求头配置与响应头查看器</h1>

        <div class="card">
            <h2>请求配置</h2>
            <input type="text" class="url-input" id="urlInput" value="https://httpbin.org/get" placeholder="输入请求URL">

            <h3 style="font-size:15px;margin-bottom:10px;color:#555;">预设头部模板</h3>
            <div class="preset-btns">
                <div class="preset-btn" onclick="applyPreset('json')">JSON API</div>
                <div class="preset-btn" onclick="applyPreset('form')">表单提交</div>
                <div class="preset-btn" onclick="applyPreset('auth')">带认证</div>
                <div class="preset-btn" onclick="applyPreset('cors')">跨域请求</div>
                <div class="preset-btn" onclick="applyPreset('cache')">缓存控制</div>
            </div>

            <h3 style="font-size:15px;margin-bottom:10px;color:#555;">自定义请求头</h3>
            <div id="headersContainer">
                <div class="header-row">
                    <input type="text" class="name-input" value="Accept" placeholder="头部名称">
                    <input type="text" class="value-input" value="application/json" placeholder="头部值">
                    <button class="remove-btn" onclick="removeRow(this)">删除</button>
                </div>
            </div>
            <button class="add-btn" onclick="addRow()">+ 添加头部</button>

            <div style="margin-top:20px;text-align:center;">
                <button class="send-btn" onclick="sendRequest()">发送请求</button>
            </div>
        </div>

        <div class="card" id="responseCard" style="display:none;">
            <h2>响应头</h2>
            <div class="response-headers" id="responseHeaders"></div>
            <div class="info-note" id="corsNote" style="display:none;">
                注意:由于浏览器CORS限制,部分响应头可能无法通过JavaScript读取。
                服务器需要设置 Access-Control-Expose-Headers 来暴露自定义头部。
            </div>
        </div>
    </div>

    <script>
        function addRow(name = '', value = '') {
            const container = document.getElementById('headersContainer');
            const row = document.createElement('div');
            row.className = 'header-row';
            row.innerHTML = `
                <input type="text" class="name-input" value="${name}" placeholder="头部名称">
                <input type="text" class="value-input" value="${value}" placeholder="头部值">
                <button class="remove-btn" onclick="removeRow(this)">删除</button>
            `;
            container.appendChild(row);
        }

        function removeRow(btn) {
            const rows = document.querySelectorAll('.header-row');
            if (rows.length > 1) btn.parentElement.remove();
        }

        function applyPreset(type) {
            const container = document.getElementById('headersContainer');
            container.innerHTML = '';

            const presets = {
                json: [
                    ['Accept', 'application/json'],
                    ['Content-Type', 'application/json']
                ],
                form: [
                    ['Content-Type', 'application/x-www-form-urlencoded']
                ],
                auth: [
                    ['Authorization', 'Bearer YOUR_TOKEN_HERE'],
                    ['Accept', 'application/json']
                ],
                cors: [
                    ['Origin', window.location.origin],
                    ['Access-Control-Request-Method', 'GET']
                ],
                cache: [
                    ['Cache-Control', 'no-cache'],
                    ['If-None-Match', '"etag_value"']
                ]
            };

            (presets[type] || []).forEach(([name, value]) => addRow(name, value));
        }

        async function sendRequest() {
            const url = document.getElementById('urlInput').value;
            const rows = document.querySelectorAll('.header-row');
            const headers = {};

            rows.forEach(row => {
                const name = row.querySelector('.name-input').value.trim();
                const value = row.querySelector('.value-input').value.trim();
                if (name && value) headers[name] = value;
            });

            try {
                const response = await fetch(url, {
                    method: 'GET',
                    headers: headers
                });

                let headerText = `<span class="header-highlight">HTTP/${response.status < 200 ? '1.1' : '1.1'}</span> <span style="color:#4caf50">${response.status} ${response.statusText}</span>\n\n`;

                response.headers.forEach((value, name) => {
                    headerText += `<span class="header-highlight">${name}:</span> <span class="value-highlight">${value}</span>\n`;
                });

                document.getElementById('responseHeaders').innerHTML = headerText;
                document.getElementById('responseCard').style.display = 'block';
                document.getElementById('corsNote').style.display = 'block';
            } catch (error) {
                document.getElementById('responseHeaders').textContent = `请求失败: ${error.message}\n\n可能原因:\n1. 目标服务器不支持CORS\n2. 网络连接问题\n3. URL格式错误`;
                document.getElementById('responseCard').style.display = 'block';
                document.getElementById('corsNote').style.display = 'block';
            }
        }
    </script>
</body>
</html>

示例3:Content-Type与请求体格式演示

代码示例

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Content-Type与请求体格式演示</title>
    <style>
        * { margin: 0; padding: 0; box-sizing: border-box; }
        body {
            font-family: 'Segoe UI', system-ui, sans-serif;
            background: #f0f2f5;
            padding: 20px;
            color: #333;
        }
        .container { max-width: 900px; margin: 0 auto; }
        h1 { text-align: center; color: #1a73e8; margin-bottom: 24px; font-size: 28px; }
        .card {
            background: #fff;
            border-radius: 12px;
            padding: 24px;
            margin-bottom: 20px;
            box-shadow: 0 2px 8px rgba(0,0,0,0.08);
        }
        .card h2 {
            font-size: 18px;
            color: #1a73e8;
            margin-bottom: 16px;
            padding-bottom: 8px;
            border-bottom: 2px solid #e8eaed;
        }
        .type-tabs {
            display: flex;
            gap: 8px;
            margin-bottom: 20px;
            flex-wrap: wrap;
        }
        .type-tab {
            padding: 10px 18px;
            border: 2px solid #e8eaed;
            border-radius: 8px;
            background: #fff;
            cursor: pointer;
            font-size: 13px;
            font-weight: 600;
            transition: all 0.2s;
        }
        .type-tab.active { background: #1a73e8; color: #fff; border-color: #1a73e8; }
        .comparison {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 16px;
        }
        .code-block {
            background: #1e1e1e;
            color: #d4d4d4;
            padding: 16px;
            border-radius: 8px;
            font-family: 'Consolas', monospace;
            font-size: 13px;
            line-height: 1.7;
            white-space: pre-wrap;
            word-break: break-all;
        }
        .block-label {
            font-size: 13px;
            font-weight: 600;
            color: #555;
            margin-bottom: 8px;
        }
        .form-demo {
            padding: 20px;
            background: #f8f9fa;
            border-radius: 8px;
            margin-top: 16px;
        }
        .form-demo label {
            display: block;
            font-weight: 600;
            margin-bottom: 4px;
            font-size: 14px;
        }
        .form-demo input {
            padding: 8px 12px;
            border: 1px solid #dadce0;
            border-radius: 6px;
            font-size: 14px;
            width: 100%;
            margin-bottom: 10px;
        }
        .form-demo button {
            padding: 8px 20px;
            background: #1a73e8;
            color: #fff;
            border: none;
            border-radius: 6px;
            cursor: pointer;
            font-weight: 600;
        }
        .result-area {
            margin-top: 12px;
            padding: 12px;
            background: #1e1e1e;
            color: #d4d4d4;
            border-radius: 6px;
            font-family: 'Consolas', monospace;
            font-size: 12px;
            white-space: pre-wrap;
            word-break: break-all;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>Content-Type与请求体格式</h1>

        <div class="card">
            <h2>选择Content-Type</h2>
            <div class="type-tabs">
                <div class="type-tab active" onclick="showType('json')">application/json</div>
                <div class="type-tab" onclick="showType('urlencoded')">x-www-form-urlencoded</div>
                <div class="type-tab" onclick="showType('multipart')">multipart/form-data</div>
                <div class="type-tab" onclick="showType('plain')">text/plain</div>
            </div>

            <div id="typeContent"></div>
        </div>

        <div class="card">
            <h2>实时演示</h2>
            <div class="form-demo">
                <label>姓名</label>
                <input type="text" id="demoName" value="张三">
                <label>邮箱</label>
                <input type="email" id="demoEmail" value="zhangsan@example.com">
                <label>年龄</label>
                <input type="number" id="demoAge" value="25">
                <button onclick="generatePayload()">生成请求体</button>
            </div>
            <div class="result-area" id="payloadResult">点击按钮生成不同Content-Type的请求体</div>
        </div>
    </div>

    <script>
        let currentType = 'json';

        const typeInfo = {
            json: {
                desc: '最常用的API请求格式。数据以JSON字符串形式发送在请求体中。',
                headers: 'Content-Type: application/json',
                body: '{"name":"张三","email":"zhangsan@example.com","age":25}',
                jsCode: `fetch('/api/users', {\n  method: 'POST',\n  headers: {\n    'Content-Type': 'application/json'\n  },\n  body: JSON.stringify({\n    name: '张三',\n    email: 'zhangsan@example.com',\n    age: 25\n  })\n});`,
                pros: ['结构化数据,支持嵌套', '前后端通用格式', '可读性好', 'API标准格式'],
                cons: ['不支持文件上传', '需要JSON.stringify转换']
            },
            urlencoded: {
                desc: 'HTML表单默认编码格式。数据以key=value形式编码,用&连接。',
                headers: 'Content-Type: application/x-www-form-urlencoded',
                body: 'name=%E5%BC%A0%E4%B8%89&email=zhangsan%40example.com&age=25',
                jsCode: `fetch('/api/users', {\n  method: 'POST',\n  headers: {\n    'Content-Type': 'application/x-www-form-urlencoded'\n  },\n  body: new URLSearchParams({\n    name: '张三',\n    email: 'zhangsan@example.com',\n    age: 25\n  }).toString()\n});`,
                pros: ['HTML表单默认格式', '简单键值对', '兼容性好'],
                cons: ['不支持文件上传', '不支持嵌套结构', '中文需要URL编码']
            },
            multipart: {
                desc: '多部分表单格式,用于文件上传。每个字段用boundary分隔符分开。',
                headers: 'Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW',
                body: '------WebKitFormBoundary7MA4YWxkTrZu0gW\nContent-Disposition: form-data; name="name"\n\n张三\n------WebKitFormBoundary7MA4YWxkTrZu0gW\nContent-Disposition: form-data; name="email"\n\nzhangsan@example.com\n------WebKitFormBoundary7MA4YWxkTrZu0gW\nContent-Disposition: form-data; name="avatar"; filename="photo.jpg"\nContent-Type: image/jpeg\n\n(二进制文件数据)\n------WebKitFormBoundary7MA4YWxkTrZu0gW--',
                jsCode: `const formData = new FormData();\nformData.append('name', '张三');\nformData.append('email', 'zhangsan@example.com');\nformData.append('avatar', fileInput.files[0]);\n\nfetch('/api/users', {\n  method: 'POST',\n  body: formData  // 注意:不要手动设置Content-Type\n});`,
                pros: ['支持文件上传', '可混合文本和文件', '浏览器自动处理boundary'],
                cons: ['请求体较大', '不要手动设置Content-Type', '解析稍复杂']
            },
            plain: {
                desc: '纯文本格式,数据以原始文本形式发送。',
                headers: 'Content-Type: text/plain',
                body: '张三\nzhangsan@example.com\n25',
                jsCode: `fetch('/api/data', {\n  method: 'POST',\n  headers: {\n    'Content-Type': 'text/plain'\n  },\n  body: '张三\\nzhangsan@example.com\\n25'\n});`,
                pros: ['最简单的格式', '无需编码转换'],
                cons: ['无结构化', '不适合复杂数据', '服务器解析困难']
            }
        };

        function showType(type) {
            currentType = type;
            document.querySelectorAll('.type-tab').forEach(t => t.classList.remove('active'));
            event.currentTarget.classList.add('active');

            const info = typeInfo[type];
            document.getElementById('typeContent').innerHTML = `
                <p style="line-height:1.8;color:#555;margin-bottom:16px;">${info.desc}</p>
                <div class="comparison">
                    <div>
                        <div class="block-label">请求头</div>
                        <div class="code-block">${info.headers}</div>
                    </div>
                    <div>
                        <div class="block-label">请求体</div>
                        <div class="code-block">${info.body}</div>
                    </div>
                </div>
                <div style="margin-top:16px;">
                    <div class="block-label">JavaScript代码</div>
                    <div class="code-block">${info.jsCode}</div>
                </div>
                <div style="margin-top:16px;display:grid;grid-template-columns:1fr 1fr;gap:16px;">
                    <div>
                        <div class="block-label" style="color:#2e7d32;">优点</div>
                        <ul style="font-size:14px;line-height:2;color:#555;">${info.pros.map(p => `<li>${p}</li>`).join('')}</ul>
                    </div>
                    <div>
                        <div class="block-label" style="color:#c62828;">缺点</div>
                        <ul style="font-size:14px;line-height:2;color:#555;">${info.cons.map(c => `<li>${c}</li>`).join('')}</ul>
                    </div>
                </div>
            `;
        }

        function generatePayload() {
            const name = document.getElementById('demoName').value;
            const email = document.getElementById('demoEmail').value;
            const age = document.getElementById('demoAge').value;
            let result = '';

            switch (currentType) {
                case 'json':
                    result = `Content-Type: application/json\n\n${JSON.stringify({ name, email, age: parseInt(age) }, null, 2)}`;
                    break;
                case 'urlencoded':
                    result = `Content-Type: application/x-www-form-urlencoded\n\n${new URLSearchParams({ name, email, age }).toString()}`;
                    break;
                case 'multipart':
                    const fd = new FormData();
                    fd.append('name', name);
                    fd.append('email', email);
                    fd.append('age', age);
                    let multipart = `Content-Type: multipart/form-data; boundary=----Boundary\n\n`;
                    for (const [key, value] of fd.entries()) {
                        multipart += `------Boundary\nContent-Disposition: form-data; name="${key}"\n\n${value}\n`;
                    }
                    multipart += `------Boundary--`;
                    result = multipart;
                    break;
                case 'plain':
                    result = `Content-Type: text/plain\n\n${name}\n${email}\n${age}`;
                    break;
            }

            document.getElementById('payloadResult').textContent = result;
        }

        showType('json');
    </script>
</body>
</html>

浏览器兼容性

特性 Chrome Firefox Safari Edge 说明
Headers API 42+ 39+ 10.1+ 14+ Fetch API的Headers对象
Content-Type 全版本 全版本 全版本 全版本 所有浏览器支持
CORS头部 全版本 全版本 全版本 全版本 浏览器自动处理
Set-Cookie SameSite 51+ 60+ 13+ 16+ SameSite属性
Cookie Priority 不支持 不支持 不支持 不支持 非标准
103 Early Hints 93+ 不支持 不支持 93+ 较新特性
自定义头部 全版本 全版本 全版本 全版本 可能触发预检

注意事项与最佳实践

1. Content-Type选择

  • API请求优先使用application/json

  • 文件上传使用multipart/form-data,不要手动设置Content-Type

  • 普通表单使用application/x-www-form-urlencoded

  • 使用FormData时浏览器自动设置Content-Type和boundary

2. 安全头部设置

  • Cookie始终设置HttpOnly和Secure属性

  • 使用SameSite=Strict或Lax防止CSRF攻击

  • 敏感Token放在Authorization头部而非URL参数

  • 避免在自定义头部中传递敏感信息

3. CORS头部注意

  • Access-Control-Allow-Origin设为*时不能携带Cookie

  • 自定义头部会触发CORS预检请求

  • 使用Access-Control-Expose-Headers暴露自定义响应头

  • 合理设置Access-Control-Max-Age减少预检请求

4. 缓存头部使用

  • 优先使用Cache-Control而非Expires

  • 静态资源使用Cache-Control: max-age=31536000, immutable配合内容哈希

  • API响应使用Cache-Control: no-cache或max-age=短时间

  • 使用ETag实现精确的缓存验证

代码规范示例

统一的请求头部管理

代码示例

class HeaderManager {
    constructor() {
        this.defaultHeaders = {
            'Accept': 'application/json',
            'Content-Type': 'application/json'
        };
    }

    // 获取带认证的头部
    getAuthHeaders() {
        const token = this.getStoredToken();
        return {
            ...this.defaultHeaders,
            'Authorization': `Bearer ${token}`,
            'X-Request-ID': crypto.randomUUID()
        };
    }

    // 获取文件上传头部(不设置Content-Type)
    getUploadHeaders() {
        const token = this.getStoredToken();
        return {
            'Accept': 'application/json',
            'Authorization': `Bearer ${token}`
        };
    }

    // 获取带缓存控制的头部
    getCacheHeaders(etag) {
        return {
            ...this.getAuthHeaders(),
            'If-None-Match': etag,
            'Cache-Control': 'no-cache'
        };
    }

    getStoredToken() {
        return localStorage.getItem('access_token') || '';
    }
}

常见问题与解决方案

问题1:自定义头部导致CORS预检

问题描述:添加Authorization或自定义X-头部后,浏览器先发送OPTIONS预检请求。

解决方案:服务器正确配置CORS头部,设置合理的Access-Control-Max-Age缓存预检结果。

问题2:FormData手动设置Content-Type

问题描述:使用FormData上传文件时手动设置Content-Type,导致boundary丢失。

解决方案:使用FormData时不要设置Content-Type,浏览器会自动设置包含boundary的正确值。

问题3:无法读取自定义响应头

问题描述:fetch API无法读取X-Request-ID等自定义响应头。

解决方案:服务器设置Access-Control-Expose-Headers: X-Request-ID, X-Total-Count暴露自定义头部。

问题4:Cookie SameSite限制

问题描述:跨站请求时Cookie不被发送。

解决方案:根据场景选择SameSite值:Strict(最严格)、Lax(默认,适中的安全级别)、None(需要配合Secure)。

总结

HTTP头部是控制Web通信行为的核心机制,本教程涵盖了以下关键内容:

  1. 内容类型头部:Content-Type决定数据格式,Accept进行内容协商

  2. 认证头部:Authorization携带凭据,Basic/Bearer/API Key等不同认证方案

  3. 缓存头部:Cache-Control是最重要的缓存控制机制,ETag/Last-Modified用于缓存验证

  4. CORS头部:Access-Control-*系列头部控制跨域访问权限

  5. Cookie头部:Cookie/Set-Cookie管理状态,HttpOnly/Secure/SameSite保障安全

  6. 自定义头部:X-前缀头部用于扩展功能,注意CORS和兼容性问题

正确使用HTTP头部可以优化性能(缓存)、保障安全(认证、CORS)、改善用户体验(内容协商),是每个前端开发者必须掌握的核心技能。

常见问题

什么是HTTP头部详解?

HTTP头部详解是HTML5开发中的重要技术,本教程详细介绍了其核心概念和实践方法。

如何学习HTTP头部详解的实际应用?

教程中提供了完整的代码示例和实践指导,建议结合示例代码动手练习。

HTTP头部详解有哪些注意事项?

常见注意事项包括兼容性、性能优化等,教程的注意事项与最佳实践部分有详细说明。

HTTP头部详解适合初学者吗?

本教程从基础概念讲起,循序渐进,适合有一定HTML和JavaScript基础的初学者。

HTTP头部详解的核心要点是什么?

核心要点包括教程简介等内容,建议按教程顺序逐步学习。

标签: Promise HTTP头部 表单提交 OAuth 响应码 网络安全 键盘输入 async await

本文由小确幸生活整理发布,转载请注明出处

本文涉及AI创作

内容由AI创作,请仔细甄别

list快速访问

上一篇: 后端通信:HTTP状态码 - 从入门到实践详解 下一篇: 后端通信:HTTPS与安全 - 从入门到实践详解

poll相关推荐