{
  "slug": "http-header-normalizer",
  "name": "gizza-ai/http-header-normalizer",
  "version": "0.1.0",
  "title": "HTTP Header Normalizer — Fix Casing, Trim Values, Sort Headers — gizza.ai",
  "description": "Canonicalize a pasted HTTP header block: Title-Case names, trimmed values, merged duplicates, sorted lines, optional redaction and a curl -H form. Free, in your browser.",
  "tags": [
    "http header normalizer",
    "canonical header casing",
    "sort http headers",
    "title case headers",
    "merge duplicate headers",
    "curl -H generator",
    "redact headers",
    "header block formatter",
    "http header cleaner"
  ],
  "category": "network",
  "urls": {
    "page": "https://gizza.ai/tools/http-header-normalizer/",
    "markdown": "https://gizza.ai/tools/http-header-normalizer/index.md",
    "descriptor": "https://gizza.ai/tools/http-header-normalizer/tool.json",
    "deep_link_example": "https://gizza.ai/tools/http-header-normalizer/?input=GET%20%2Fv1%2Fitems%3Fpage%3D2%20HTTP%2F1.1%0Ahost%3A%20%20%20api.example.com%0AACCEPT%3A%20application%2Fjson%0Aaccept%3A%20text%2Fplain%0Ax-request-id%3A%20%20%209f3c%0Acontent-type%3Aapplication%2Fjson&case=canonical&sort=name&duplicates=combine&unfold=true&drop_empty=true&drop_headers=authorization%2Ccookie%2Cx-internal-token&keep_headers=host%2Ccontent-type&output=headers"
  },
  "cli": "gizza tool http-header-normalizer \"GET /v1/items?page=2 HTTP/1.1\nhost:   api.example.com\nACCEPT: application/json\naccept: text/plain\nx-request-id:   9f3c\ncontent-type:application/json\"",
  "tool": {
    "description": "Turn a pasted HTTP header block into one canonical block. Rewrites field names to a chosen casing (canonical Title-Case by default, including the names plain title-casing gets wrong such as ETag, WWW-Authenticate, DNT and Sec-WebSocket-Key; or lower, upper, or preserved), trims every value, joins obsolete indented continuation lines, folds repeated names case-insensitively (comma-joined per RFC 7230 by default, or list/first/last, with Set-Cookie never comma-joined), drops empty values, applies a comma-separated deny list or allowlist with 'x-*' prefix wildcards, and sorts the fields by name so two captures of the same request diff cleanly. An optional leading request or status line is kept verbatim on top and HTTP/2 pseudo-headers are sorted first and left lowercase. Returns the normalized header block, the same headers as copy-pasteable curl -H flags, or a CSV summary of the run.",
    "parameters": {
      "additionalProperties": false,
      "properties": {
        "case": {
          "default": "canonical",
          "description": "How field names are spelled on the way out: 'canonical' (default) is HTTP Title-Case with the exception table plain title-casing gets wrong — ETag, WWW-Authenticate, DNT, TE, Content-MD5, X-XSS-Protection, Sec-WebSocket-*, X-UA-Compatible and friends; 'lower' is the HTTP/2 and proxy-log form; 'upper' suits CGI-style comparisons; 'preserve' keeps the casing of the first time each name appears. HTTP/2 pseudo-headers stay lowercase in every mode, because the protocol requires it.",
          "enum": [
            "canonical",
            "lower",
            "upper",
            "preserve"
          ],
          "type": "string"
        },
        "drop_empty": {
          "default": false,
          "description": "Remove fields whose value is empty after trimming, such as a bare 'X-Debug:' line. Off by default, because an empty value is still a header that was sent and can be meaningful. Turn it on to clear the leftovers a proxy or an unfilled template appends.",
          "type": "boolean"
        },
        "drop_headers": {
          "default": "",
          "description": "Field names to remove, comma-separated and matched case-insensitively — e.g. 'cookie,authorization,x-internal-token'. A trailing '*' makes it a prefix rule, so 'x-*' drops every X- header. Useful for redacting credentials before pasting a capture into a bug report.",
          "type": "string"
        },
        "duplicates": {
          "default": "combine",
          "description": "How repeated field names (compared case-insensitively) are folded: 'combine' (default) joins their values with ', ' per RFC 7230 §3.2.2; 'list' keeps every occurrence on its own line; 'first' keeps only the first value; 'last' keeps only the last. Set-Cookie is never comma-joined — RFC 6265 forbids it, so it always stays one line per cookie.",
          "enum": [
            "combine",
            "list",
            "first",
            "last"
          ],
          "type": "string"
        },
        "input": {
          "description": "The HTTP header block to normalize, one 'Name: value' per line — e.g. 'host:  example.com' then 'CONTENT-TYPE: application/json'. A leading request line ('GET /p HTTP/1.1') or status line ('HTTP/1.1 200 OK') is optional and is kept verbatim at the top. Indented continuation lines are joined onto the header above them, HTTP/2 pseudo-headers such as ':method' are understood, and the first blank line ends the block so a pasted body is ignored. Max 20,000 lines and 1,000,000 bytes per run.",
          "type": "string"
        },
        "keep_headers": {
          "default": "",
          "description": "An allowlist: when set, ONLY these field names survive and everything else is dropped — comma-separated, case-insensitive, with the same trailing-'*' prefix rule as drop_headers. Empty by default, which keeps everything. Applied before drop_headers, so the two can be combined.",
          "type": "string"
        },
        "output": {
          "default": "headers",
          "description": "What to return: 'headers' (default) is the normalized header block as text; 'curl' is the same headers as copy-pasteable '-H' flags, single-quoted and backslash-continued (pseudo-headers are skipped because curl derives them from the URL and method); 'summary' is a metric,value CSV of the run — lines in, headers in, names in, headers out, names recased, values trimmed, duplicates merged, names dropped, and body lines skipped.",
          "enum": [
            "headers",
            "curl",
            "summary"
          ],
          "type": "string"
        },
        "sort": {
          "default": "name",
          "description": "Line order in the result: 'name' (default) sorts alphabetically by lower-cased field name, which is what makes two captures of the same request diff cleanly; 'none' keeps the order you pasted. Pseudo-headers are placed before regular fields when sorting, per RFC 9113. Sorting is stable, so repeats of one name keep their relative order.",
          "enum": [
            "name",
            "none"
          ],
          "type": "string"
        },
        "unfold": {
          "default": true,
          "description": "Join obsolete line folds: a continuation line that starts with a space or tab is appended to the header above it with a single space, which is how a modern parser reads it. On by default. Turn it off to keep the fold as an indented second line in the output. A continuation with no header before it is an error either way.",
          "type": "boolean"
        }
      },
      "required": [
        "input"
      ],
      "type": "object"
    }
  }
}