RepoPilotOpen in app →

julienschmidt/httprouter

A high performance HTTP request router that scales well

Mixed

Stale — last commit 2y ago

worst of 4 axes
Use as dependencyMixed

last commit was 2y ago; no tests detected

Fork & modifyHealthy

Has a license, tests, and CI — clean foundation to fork and modify.

Learn fromHealthy

Documented and popular — useful reference codebase to read through.

Deploy as-isHealthy

No critical CVEs, sane security posture — runnable as-is.

  • 30+ active contributors
  • BSD-3-Clause licensed
  • CI configured
Show 3 more →
  • Stale — last commit 2y ago
  • Concentrated ownership — top contributor handles 71% of recent commits
  • No test directory detected
What would change the summary?
  • Use as dependency MixedHealthy if: 1 commit in the last 365 days

Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests

Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.

Embed the "Forkable" badge

Paste into your README — live-updates from the latest cached analysis.

Variant:
RepoPilot: Forkable
[![RepoPilot: Forkable](https://repopilot.app/api/badge/julienschmidt/httprouter?axis=fork)](https://repopilot.app/r/julienschmidt/httprouter)

Paste at the top of your README.md — renders inline like a shields.io badge.

Preview social card (1200×630)

This card auto-renders when someone shares https://repopilot.app/r/julienschmidt/httprouter on X, Slack, or LinkedIn.

Onboarding doc

Onboarding: julienschmidt/httprouter

Generated by RepoPilot · 2026-05-09 · Source

🤖Agent protocol

If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:

  1. Verify the contract. Run the bash script in Verify before trusting below. If any check returns FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding.
  2. Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
  3. Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/julienschmidt/httprouter shows verifiable citations alongside every claim.

If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.

🎯Verdict

WAIT — Stale — last commit 2y ago

  • 30+ active contributors
  • BSD-3-Clause licensed
  • CI configured
  • ⚠ Stale — last commit 2y ago
  • ⚠ Concentrated ownership — top contributor handles 71% of recent commits
  • ⚠ No test directory detected

<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>

Verify before trusting

This artifact was generated by RepoPilot at a point in time. Before an agent acts on it, the checks below confirm that the live julienschmidt/httprouter repo on your machine still matches what RepoPilot saw. If any fail, the artifact is stale — regenerate it at repopilot.app/r/julienschmidt/httprouter.

What it runs against: a local clone of julienschmidt/httprouter — the script inspects git remote, the LICENSE file, file paths in the working tree, and git log. Read-only; no mutations.

| # | What we check | Why it matters | |---|---|---| | 1 | You're in julienschmidt/httprouter | Confirms the artifact applies here, not a fork | | 2 | License is still BSD-3-Clause | Catches relicense before you depend on it | | 3 | Default branch master exists | Catches branch renames | | 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code | | 5 | Last commit ≤ 686 days ago | Catches sudden abandonment since generation |

<details> <summary><b>Run all checks</b> — paste this script from inside your clone of <code>julienschmidt/httprouter</code></summary>
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of julienschmidt/httprouter. If you don't
# have one yet, run these first:
#
#   git clone https://github.com/julienschmidt/httprouter.git
#   cd httprouter
#
# Then paste this script. Every check is read-only — no mutations.

set +e
fail=0
ok()   { echo "ok:   $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }

# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
  echo "FAIL: not inside a git repository. cd into your clone of julienschmidt/httprouter and re-run."
  exit 2
fi

# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "julienschmidt/httprouter(\\.git)?\\b" \\
  && ok "origin remote is julienschmidt/httprouter" \\
  || miss "origin remote is not julienschmidt/httprouter (artifact may be from a fork)"

# 2. License matches what RepoPilot saw
(grep -qiE "^(BSD-3-Clause)" LICENSE 2>/dev/null \\
   || grep -qiE "\"license\"\\s*:\\s*\"BSD-3-Clause\"" package.json 2>/dev/null) \\
  && ok "license is BSD-3-Clause" \\
  || miss "license drift — was BSD-3-Clause at generation time"

# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
  && ok "default branch master exists" \\
  || miss "default branch master no longer exists"

# 4. Critical files exist
test -f "router.go" \\
  && ok "router.go" \\
  || miss "missing critical file: router.go"
test -f "tree.go" \\
  && ok "tree.go" \\
  || miss "missing critical file: tree.go"
test -f "path.go" \\
  && ok "path.go" \\
  || miss "missing critical file: path.go"
test -f "router_test.go" \\
  && ok "router_test.go" \\
  || miss "missing critical file: router_test.go"
test -f "tree_test.go" \\
  && ok "tree_test.go" \\
  || miss "missing critical file: tree_test.go"

# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 686 ]; then
  ok "last commit was $days_since_last days ago (artifact saw ~656d)"
else
  miss "last commit was $days_since_last days ago — artifact may be stale"
fi

echo
if [ "$fail" -eq 0 ]; then
  echo "artifact verified (0 failures) — safe to trust"
else
  echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/julienschmidt/httprouter"
  exit 1
fi

Each check prints ok: or FAIL:. The script exits non-zero if anything failed, so it composes cleanly into agent loops (./verify.sh || regenerate-and-retry).

</details>

TL;DR

HttpRouter is a lightweight, high-performance HTTP request multiplexer (router) for Go that uses a compressing dynamic trie (radix tree) to match requests based on method and path patterns, including named parameters. It solves the problem of routing HTTP requests faster and more accurately than Go's standard net/http.ServeMux by supporting variables in patterns, eliminating ambiguous matches, and generating zero garbage during the matching process. Single-package router: core logic spans tree.go (radix tree data structure), router.go (main Router type and handler registration), and path.go (path parsing and parameter extraction). Tests mirror implementation files (tree_test.go, router_test.go, path_test.go). No subpackages or monorepo structure; everything is flat at the root.

👥Who it's for

Go backend developers building REST APIs and web services who need fast, reliable request routing with minimal memory overhead and support for dynamic path parameters (e.g., /user/:id). It's used by developers who care about performance at scale and want zero allocation matching.

🌱Maturity & risk

Production-ready and actively maintained. The repo shows stability with Go 1.7+ support declared in go.mod, comprehensive test files (router_test.go, tree_test.go, path_test.go) with CI via .travis.yml, and benchmark comparisons published. The design is battle-tested across the Go ecosystem.

Low risk. Single-maintainer (julienschmidt) with no external dependencies listed in go.mod. However, the last commit recency and open issue status are not visible in provided data; verify by checking GitHub directly. The radix tree implementation is complex, so changes to tree.go warrant careful review and test coverage verification.

Active areas of work

Unable to determine from provided data. Check GitHub's commits, pull requests, and issues tabs for recent activity. The .travis.yml presence indicates CI runs on changes.

🚀Get running

git clone https://github.com/julienschmidt/httprouter.git
cd httprouter
go test ./...

Daily commands: No dev server to run. This is a library package. Run tests with go test -v or go test -bench=. -benchmem to see performance. See router_test.go for example usage patterns.

🗺️Map of the codebase

  • router.go — Core Router type and request matching entry point; every contributor must understand how routes are registered and dispatched
  • tree.go — Radix tree implementation for path matching; the heart of the performance optimization and pattern matching logic
  • path.go — Path parsing and parameter extraction utilities; handles named and wildcard parameter resolution in URLs
  • router_test.go — Comprehensive routing test suite demonstrating API usage patterns and expected behavior
  • tree_test.go — Radix tree algorithm validation; critical for understanding correctness of the high-performance matching engine

🧩Components & responsibilities

  • Router (Go net/http, radix tree) — Registers routes by HTTP method, implements http.Handler, dispatches matched requests to handlers
    • Failure mode: Returns 404 if no route matches; panic if route registration conflicts (e.g., two handlers for same path+method)
  • Radix Tree (Compressing trie algorithm) — Stores routes as compressed trie nodes, performs O(k) lookup by path and method, extracts named/wildcard parameters
    • Failure mode: Panics on conflicting route registration; returns nil handler on no match
  • Path Parser (String segmentation, parameter mapping) — Tokenizes URL paths into segments, identifies parameter markers (:name, *path), extracts parameter values from request paths
    • Failure mode: Invalid parameter syntax (e.g., ':' without name) causes panic during route registration; mis-matched route patterns cause 404

🔀Data flow

  • Client HTTP requestRouter.ServeHTTP — net/http server passes request to router as http.Handler
  • RouterRadix Tree lookup — Router queries tree with request method and path
  • Radix TreePath Parser — Tree traversal uses path parser to segment and match URL segments
  • Radix TreeHandler + Params — Tree returns matched handler function and extracted parameters (Params map)
  • Handler + ParamsUser handler function — Router invokes handler with ResponseWriter, Request, and Params containing :name and *path values

🛠️How to make changes

Add a new HTTP route with path parameters

  1. Call router.GET/POST/PUT/DELETE with a pattern containing :paramName or *catchall (router.go)
  2. The pattern is parsed by path.go into segments with parameter markers (path.go)
  3. The pattern is inserted into the radix tree in tree.go as a compressed trie node (tree.go)
  4. At runtime, incoming requests are matched via tree traversal; parameters are extracted and passed to your handler (router.go)

Optimize routing for a large number of similar paths

  1. Understand the radix tree compressing behavior in tree.go—adjacent single-child nodes are merged (tree.go)
  2. Group routes by common prefix to maximize compression; e.g., /api/v1/users, /api/v1/posts share /api/v1/ (router.go)
  3. Avoid excessive wildcard routes at high levels; they reduce tree compression efficiency (path.go)

Debug route matching failures

  1. Check path.go for parameter name syntax errors (:paramName must be alphanumeric after the colon) (path.go)
  2. Verify no conflicting routes in tree.go—httprouter requires explicit, non-ambiguous patterns (tree.go)
  3. Review router_test.go for examples of valid pattern registration and matching (router_test.go)

🔧Why these technologies

  • Radix Tree (Compressing Trie) — Efficient O(k) path matching where k is key length, not O(n) linear scan; memory-efficient due to node compression; scales well with long paths and many routes
  • *Named parameters (:name) + wildcards (path) — Explicit, non-ambiguous routing patterns; avoids awkward priority rules of http.ServeMux; maps cleanly to handler function signatures
  • Method-aware routing (GET/POST/etc.) — RESTful patterns require HTTP verb differentiation; http.ServeMux does not; enables same path to handle multiple verbs with different handlers

⚖️Trade-offs already made

  • No wildcard or regex pattern prefix matching

    • Why: Simplifies tree construction and avoids ambiguous match priority rules; reduces complexity and mental overhead for users
    • Consequence: Routes must be explicit and fully specified; users cannot use patterns like /api/* to match all API routes, must register each one or use catch-all *path
  • Single radix tree per method per router instance

    • Why: Minimal memory footprint and fast lookup; single tree structure shared across requests
    • Consequence: Router is thread-safe for reads (route matching) after initialization, but route registration is not concurrent-safe; all routes must be registered before concurrent serving begins
  • No built-in middleware or chain handling

    • Why: Keep router lightweight and focused; middleware is orthogonal concern
    • Consequence: Middleware must be implemented at call site or wrapped around handler; router does not provide before/after hooks

🚫Non-goals (don't propose these)

  • Does not provide middleware or request/response hooks
  • Does not support regex patterns or prefix wildcards for matching
  • Does not include authentication, logging, or other cross-cutting concerns
  • Does not offer concurrent route registration; routes must be registered before concurrent request handling

📊Code metrics

  • Avg cyclomatic complexity: ~6 — Radix tree construction and lookup involve recursive traversal with pointer indirection, string segmentation, and parameter mapping; moderate algorithmic complexity mitigated by compression
  • Largest file: tree.go (450 lines)
  • Estimated quality issues: ~2 — Minimal error handling for invalid route patterns; no logging or debugging output; reliance on panics for conflict detection; otherwise clean, well-structured code

⚠️Anti-patterns to avoid

  • Panic on route conflicts (Medium)tree.go (node insertion): Router panics if two routes conflict (e.g., /users/:id and /users/:uid for same path); no graceful error handling or conflict resolution
  • No concurrent route registration safety (High)router.go (Handle methods): Route registration is not protected by locks; concurrent calls to Handle/GET/POST during initial setup can cause data races or silent overwrites
  • Implicit catch-all priority (Low)tree.go, path.go: Wildcard routes (*path) and named parameters (:name) have implicit priority rules that may not be obvious to users; documentation should clarify

🔥Performance hotspots

  • tree.go (tree traversal) (Algorithmic) — Lookup performance degrades with very deep paths or many similar prefixes; radix tree compression helps but cannot eliminate worst case
  • path.go (parameter extraction) (Per-request) — For routes with many named parameters, string splitting and map population occurs per request; negligible for typical routes but scales with param count

🪤Traps & gotchas

No hidden env vars or config quirks observed. However: (1) RedirectTrailingSlash and RedirectFixedPath are opt-in features—disabled by default; (2) path parameters are case-sensitive by default; (3) The 3-argument API omits context.Context, so middleware using ctx.Value() won't work without the Handler/HandlerFunc wrappers; (4) Radix tree compression is automatic but node splitting during insertion can impact exact traversal order in edge cases.

🏗️Architecture

💡Concepts to learn

  • Radix Tree (Compressed Trie) — The core data structure of httprouter that achieves O(k) path matching (k = path length) by compressing path segments; understanding this is essential to grasping why the router scales so well
  • Zero-Copy / Zero-Allocation Routing — HttpRouter's defining performance feature—path matching generates no heap allocations except for parameter slices; critical for understanding the 3-argument API vs Handler API trade-offs
  • HTTP Method-Based Multiplexing — Unlike net/http.ServeMux, httprouter routes on both path AND HTTP method (GET, POST, etc.) in a single trie structure, eliminating the need for separate per-method handling
  • Path Parameter Extraction — Httprouter's named parameters (:id, :name) are extracted during trie traversal without regex compilation or post-processing, making them very cheap compared to other routers
  • Trailing Slash Normalization & Path Correction — The router can automatically redirect /user/1/ to /user/1 and vice versa, and fix case mismatches; understanding RedirectTrailingSlash and RedirectFixedPath flags is essential for production deployments
  • Trie Node Compression & Path Segment Merging — Httprouter's radix tree automatically merges single-child nodes into longer path segments to reduce tree depth and memory overhead—this is the 'compressing' part that distinguishes it from standard tries
  • gorilla/mux — Popular alternative Go router with regex pattern matching and middleware support—often compared to httprouter for different use cases
  • labstack/echo — Full web framework for Go that wraps a high-performance router; users evaluating httprouter often compare it to Echo's routing layer
  • gin-gonic/gin — Another fast Go web framework with a router inspired by similar radix tree patterns; competitors in the performance-conscious ecosystem
  • valyala/fasthttp — Lower-level HTTP request handler for Go with its own routing; users wanting extreme performance optimization often layer httprouter on fasthttp
  • julienschmidt/go-http-routing-benchmark — Official benchmarking suite by the same author comparing httprouter against other Go routers—essential reference for performance claims

🪄PR ideas

To work on one of these in Claude Code or Cursor, paste: Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.

Add benchmarks for path matching performance with various tree depths and route counts

The README emphasizes 'high performance' and 'scales well', but there are no benchmark files to measure performance characteristics. A contributor could add comprehensive benchmarks in a new bench_test.go file to validate performance claims and prevent performance regressions. This would be particularly valuable for a router library where performance is a core selling point.

  • [ ] Create bench_test.go file with benchmarks for tree construction (NewRouter)
  • [ ] Add benchmarks for path matching under various scenarios: shallow trees, deep trees (nested parameters), and wide trees (many siblings)
  • [ ] Add benchmarks comparing performance with different route patterns (static routes, single parameters, multiple parameters)
  • [ ] Document benchmark results in README.md with a 'Performance' section referencing the benchmarks

Add comprehensive tests for edge cases in path.go parameter parsing

The path.go file handles critical path parameter parsing logic, but path_test.go likely needs expanded coverage for edge cases. Given that path matching is the core responsibility, additional tests for malformed paths, empty parameters, consecutive wildcards, and UTF-8 edge cases would improve robustness and prevent subtle bugs.

  • [ ] Review path_test.go for coverage gaps and add tests for: empty parameter names, consecutive colons/wildcards, trailing slashes in patterns
  • [ ] Add tests for Unicode/UTF-8 paths and non-ASCII parameter values
  • [ ] Add tests for path normalization edge cases (multiple slashes, dot segments if supported)
  • [ ] Run go test -cover to verify coverage improvements in path.go

Add GitHub Actions workflow to replace .travis.yml and test against multiple Go versions

The repo uses an outdated .travis.yml for CI, but Go 1.7 is ancient (released 2016). A modern GitHub Actions workflow would test against recent and supported Go versions (1.19+, 1.20, 1.21), ensure compatibility, and catch regressions. This modernizes the project's CI/CD without adding unnecessary tooling.

  • [ ] Create .github/workflows/test.yml with matrix testing for Go versions 1.19, 1.20, 1.21, and latest
  • [ ] Include steps for: go test -v -race ./..., go vet ./..., and optional linting (golangci-lint)
  • [ ] Add badge to README.md showing workflow status
  • [ ] Optionally deprecate .travis.yml with a comment or remove if maintainers agree

🌿Good first issues

  • Add benchmarks to path_test.go comparing parameter extraction performance across different path segment counts—current repo has router_test.go benchmarks but path.go parsing benchmarks are missing.
  • Document the exact algorithm and complexity analysis for tree node compression in tree.go with inline comments and a DESIGN.md file explaining why this radix tree design beats standard tries.
  • Add examples/ directory with runnable code samples (e.g., examples/simple.go showing basic GET/POST routes, examples/params.go for named parameters, examples/middleware.go for handler wrapping).

Top contributors

Click to expand

📝Recent commits

Click to expand
  • 4840180 — fix: only append MethodOptions as allowed method if HandleOPTIONS is true (#360) (Conni2461)
  • ef8578d — Explained how to install the package to guide beginners (#341) (htmfilho)
  • 0a59d7f — Added Intake to web frameworks based on HttpRouter (#356) (dbubel)
  • d4aabdb — Added Jett to web frameworks based on HttpRouter (#355) (saurabh0719)
  • 2dc7cb6 — tree: fix unnecessary type conversion (#365) (lnnt)
  • a145652 — Update README.md - add nchi (#368) (muir)
  • f0e42ac — README: Remove Travis Badge (julienschmidt)
  • 3425025 — Fix: #322 (#342) (Fov6363)
  • 829d723 — Use pkg.go.dev domain (#346) (josemukorivo)
  • fe77dd0 — Support TSR when wildcard follows named param (#321) (erichuang1994)

🔒Security observations

The httprouter codebase demonstrates good security practices overall. As a lightweight routing library with minimal dependencies, it has a small attack surface. The primary concern is the severely outdated Go version (1.7 from 2016) specified in go.mod, which should be updated immediately to a modern, supported version. No hardcoded secrets, injection vulnerabilities, or other critical issues were identified in the provided file structure. The library's focused scope (HTTP routing only) and lack of external dependencies further reduce security risk. Consider establishing a formal security policy to facilitate responsible vulnerability disclosure.

  • Low · Outdated Go Version Specification — go.mod. The go.mod file specifies 'go 1.7', which was released in 2016 and is now severely outdated. This version is no longer supported and likely contains numerous security vulnerabilities and bugs that have been patched in newer Go versions. Fix: Update the Go version requirement to a currently maintained version (1.21 or later). Run 'go mod tidy' after updating to ensure compatibility.
  • Low · Missing Security Policy Documentation — Repository root. The repository lacks a SECURITY.md file or security policy. This makes it difficult for security researchers to report vulnerabilities responsibly. Fix: Create a SECURITY.md file documenting how to report security issues responsibly, following GitHub's security policy guidelines.

LLM-derived; treat as a starting point, not a security audit.


Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.

Mixed signals · julienschmidt/httprouter — RepoPilot