DEV Community

SEN LLC
SEN LLC

Posted on

A Searchable HTTP Status Code Reference with 36 Codes, One-Line Descriptions, and Yes, 418

A Searchable HTTP Status Code Reference with 36 Codes, One-Line Descriptions, and Yes, 418

Is 401 or 403 the "not authenticated" one? Which RFC defines 418? What even is 226? I kept leaving my editor to look these up and decided to build a single-page reference I could actually keep open in a tab. Vanilla JS, 36 codes, one-line descriptions, search on code / name / keyword.

Every developer has a mental cache of HTTP status codes that goes exactly as far as 200, 301, 404, and 500. Anything else gets looked up. I got tired of opening a new tab every time I needed to confirm that 403 is "authenticated but forbidden" and 401 is "not authenticated," so I built a small single-page reference.

πŸ”— Live demo: https://sen.ltd/portfolio/http-status-codes/
πŸ“¦ GitHub: https://github.com/sen-ltd/http-status-codes

Screenshot

36 codes with one-line descriptions, category color-coding (1xx-5xx), RFC references, and search that hits the code number, name, or any word in the description. Vanilla JS, zero deps, no build.

Five fields per code, one line of description

The data model is minimal:

export const CODES = [
  { code: 200, name: 'OK', rfc: 'RFC 9110',
    ja: '成功',
    en: 'Standard success.' },
  { code: 401, name: 'Unauthorized', rfc: 'RFC 9110',
    ja: 'θͺθ¨ΌγŒεΏ…θ¦γ€γΎγŸγ―θͺθ¨Όζƒ…ε ±γŒη„‘効',
    en: 'Authentication required or credentials rejected.' },
  { code: 403, name: 'Forbidden', rfc: 'RFC 9110',
    ja: 'θͺθ¨ΌζΈˆγΏγ γŒζ¨©ι™γͺし',
    en: 'Authenticated but access denied.' },
  { code: 418, name: "I'm a teapot", rfc: 'RFC 2324',
    ja: 'γƒ†γ‚£γƒΌγƒγƒƒγƒˆγ«γ‚³γƒΌγƒ’γƒΌγ‚’ζ·Ήγ‚Œγ•γ›γ‚ˆγ†γ¨γ—γŸ',
    en: 'The server refuses to brew coffee because it is a teapot.' },
  // ...
]
Enter fullscreen mode Exit fullscreen mode

Each description is one line β€” this is the single most important constraint. Long paragraphs make the list unreadable at a glance. A one-line description works as both a tooltip and a glanceable reference.

The 401/403 distinction is the single most looked-up HTTP question I know of, and a lot of references wave their hands at it. Making that distinction crisp ("authenticated but forbidden" vs "authentication required or rejected") was deliberate β€” it's the sentence that justifies the reference existing.

Search hits code, name, and both description languages

The search input should be able to find a code regardless of what the user remembers:

function matches(item, query) {
  const q = query.trim().toLowerCase()
  if (!q) return true
  return (
    String(item.code).includes(q) ||
    item.name.toLowerCase().includes(q) ||
    item.ja.includes(q) ||
    item.en.toLowerCase().includes(q)
  )
}
Enter fullscreen mode Exit fullscreen mode

String(item.code).includes(q) coerces the number to a string so partial matches work ("40" brings up the entire 400s). Typing "too many" finds 429 Too Many Requests. Typing "θͺθ¨Ό" (authentication) finds 401, 403, and 407. One input, four orthogonal match axes, no filter UI to learn.

Small-reference tools live and die on this kind of input handling. You want the user to type the first thing that comes to mind and get a result. Explicit "filter by X" dropdowns are for tools with dozens of axes; for three dozen codes, one free-text box is better.

Color-coding by dividing by 100

HTTP status categories are the hundreds digit. So:

function categoryOf(code) {
  return Math.floor(code / 100)
}

const COLORS = {
  1: 'var(--color-1xx)',  // informational: gray
  2: 'var(--color-2xx)',  // success: green
  3: 'var(--color-3xx)',  // redirect: blue
  4: 'var(--color-4xx)',  // client error: orange
  5: 'var(--color-5xx)',  // server error: red
}
Enter fullscreen mode Exit fullscreen mode

Each card gets a 4-6px colored stripe on the left edge, which is enough to recognize "run of red stripes = 5xx section" from across the page. I tried splitting the grid by category section (separate "2xx Success" and "3xx Redirection" headers), but that falls apart the moment a search crosses categories. Continuous grid + color stripe wins because both the unfiltered list and any filtered result stay visually coherent.

RFC numbers as text, not links

I was tempted to make RFC 9110 a hyperlink to the IETF spec. I didn't:

  1. External links degrade a reference tool into a trampoline. The user came here to find out the answer, not to be shipped somewhere else.
  2. Most people who know "RFC 9110" can paste that into their preferred search engine.
  3. Keeping the tool fully self-contained preserves the offline-usable story.

Just the number as text is enough. There's a real lesson here: "helpful" features can subtract value when they pull focus away from the core job. A reference you have to leave to use the reference is one you'll stop opening.

Why 418 is in there

I'm a teapot made it in, of course:

{ code: 418, name: "I'm a teapot", rfc: 'RFC 2324',
  ja: 'γƒ†γ‚£γƒΌγƒγƒƒγƒˆγ«γ‚³γƒΌγƒ’γƒΌγ‚’ζ·Ήγ‚Œγ•γ›γ‚ˆγ†γ¨γ—γŸ',
  en: 'The server refuses to brew coffee because it is a teapot.' },
Enter fullscreen mode Exit fullscreen mode

418 comes from the 1998 April Fools' RFC 2324 (Hyper Text Coffee Pot Control Protocol). It's an official IETF joke, and it's registered with IANA as a proper HTTP status code. Browsers handle it, fetch returns it, Chrome DevTools shows it. Leaving it out of a reference because it's unserious would be revisionist history β€” every real reference I trust keeps it.

Tests

13 cases on node --test, half of them verifying the data and half verifying the search logic:

test('every code is a valid HTTP status number', () => {
  for (const c of CODES) {
    assert.ok(c.code >= 100 && c.code < 600, `invalid code: ${c.code}`)
  }
})

test('every code has non-empty name, ja, en', () => {
  for (const c of CODES) {
    assert.ok(c.name.length > 0)
    assert.ok(c.ja.length > 0)
    assert.ok(c.en.length > 0)
  }
})

test('no duplicate codes', () => {
  const seen = new Set()
  for (const c of CODES) {
    assert.ok(!seen.has(c.code), `duplicate code: ${c.code}`)
    seen.add(c.code)
  }
})

test('search by code number', () => {
  const r = CODES.filter((c) => matches(c, '401'))
  assert.equal(r.length, 1)
  assert.equal(r[0].name, 'Unauthorized')
})

test('search finds 429 by keyword', () => {
  const r = CODES.filter((c) => matches(c, 'too many'))
  assert.ok(r.some((c) => c.code === 429))
})
Enter fullscreen mode Exit fullscreen mode

Data tests catch the kind of bugs that cost zero cognitive effort to introduce and cost you real debugging time later. They're the cheapest line of defense.

Series

This is entry #14 in my 100+ public portfolio series.

If I'm missing a code you use regularly, issues welcome.

Top comments (0)