Changelog
Technical changes shipped to Quickmock — features, fixes, security hardening. Source-of-truth is the git history; this page is the curated developer-facing view.
2026-10-17 — Abuse hardening
Security
- Header values with control characters are rejected. A mock header value containing a line break or another control character (an attempt to smuggle an extra header) now fails validation with a clear error everywhere headers are set — response headers, variants, routes, sequence steps and the error response. Such values were already neutralised on the wire.
- Mocks that impersonate an LLM API to make an AI agent run shell commands are blocked. The anti-spam filter now rejects tool calls to shell-execution tools (OpenAI
tool_calls / function_call, Anthropic tool_use) and sandbox network reconnaissance. Ordinary tool-call mocks such as get_weather keep working.
2026-10-02 — Leaner internals; clearing logs resets the counter
Changed
- robots.txt is one rule group instead of the same rules repeated for 23 named crawlers — every bot, AI crawlers included, still gets exactly the same access. The guide and template lists in
llms.txt are now generated from the same registry as the pages and the sitemap, so they can no longer drift out of date.
- The cross-site guard on mock creation now uses Go’s built-in CSRF protection. Browser requests from another site — including a sibling subdomain — are rejected; curl, CI and other non-browser clients are unaffected. The language switcher endpoint accepts only its own form, no longer a JSON body.
- Internal clean-up: about 700 lines of hand-rolled code were replaced by the Go standard library and the database driver — no change to the API or to how mocks behave.
Fixed
- Clearing a mock’s logs now also resets its request counter. Before, the log list emptied but the summary kept showing the old request count and last-request time.
GET /api/mocks/:id/logs returns "logs": [] for a mock with no captured requests, instead of null.
- The live request inspector streams from the first visit. On a private mock the page opened its live connection before the inspector session existed, got refused and quietly fell back to re-asking the server every 2 seconds; it now connects once the session is ready, and a locked inspector no longer polls at all. “Clear logs” also refreshes the list at once, and the page no longer logs an htmx error on every load.
2026-09-26 — No payment requests; inspector polling fix
Security
- Quickmock no longer hosts x402 payment requests on real networks. A mock can't verify a payment, so a
402 reply asking for real stablecoins was a trap for any agent that paid it — and someone had started listing such mocks in an x402 discovery catalog. Creating or editing a mock that carries an x402 payment requirement on a mainnet (Base, Ethereum, Solana…) now fails with 422 payment_blocked. x402 on testnets such as base-sepolia stays allowed, and so does a plain 402 status.
Fixed
- An inspector tab left open on a deleted or expired mock no longer polls forever. The request list and the summary counter re-requested the server every 2–5 seconds and got a 404 that the page silently ignored — one forgotten tab made over two thousand requests in a few days. The server now tells the page to stop polling, and the page says the mock is gone.
2026-09-20 — Trustworthy client IPs
Security
- The origin now accepts web traffic from Cloudflare only, at two independent layers: a firewall allow-list of Cloudflare's published ranges, refreshed weekly by a timer, and Authenticated Origin Pulls, where the web server demands Cloudflare's TLS client certificate. This is what makes the visitor's address trustworthy — Cloudflare overwrites the header carrying it, but only for traffic that actually goes through Cloudflare. Until the direct path was closed, anyone who found the origin could connect to it and claim to be any IP at all.
Fixed
- The per-IP cap on active mocks, the rate limit and the operator blocklist were all keying on an internal address rather than the visitor's, which collapsed three per-IP controls into a single global bucket: a blocklist that matched nobody, and one caller able to spend everyone's quota. Request logs showed the same useless address. All of it now resolves to the real client IP.
- Commands inside a guide's long-form sections rendered as plain paragraph text — no code styling, no copy button — even though they are exactly the lines a reader is meant to type. They now look like every other code block on the page and carry the same one-click copy.
Added
- The scripts and the full write-up for this are in the repository under
deploy/cloudflare/ — why each layer is needed, the order that avoids taking a site down, how the weekly sync fails safe, and the pitfalls worth knowing if you run something behind a CDN yourself.
2026-09-19 — Status filtering and new locales
Added
- Status Filter: The request inspector now captures the actually-served HTTP status for each request, and allows filtering the log view by status code. Log exports include the new field.
- Locales: Added full translations for Spanish, Portuguese, and Simplified Chinese, making Quickmock accessible to a broader global audience based on traffic trends.
- Public API and CLI:
go install .../cmd/quickmock@latest gives you a quickmock create command that prints a mock's public URL and its one-time admin token, so a CI job can get a throwaway endpoint without a browser. The new Public API and CLI guide covers installation, the flags, and the curl equivalent for pipelines that would rather not install anything.
Fixed
- Status filtering shipped with three rough edges, now sanded down: the status badge had no colours defined, so a 500 looked exactly like a 200; a status outside the dropdown's shortlist left the control reading all statuses above a list that was in fact filtered; and
?status= on the log export silently ignored garbage instead of answering 422 the way ?method= already did.
- Self-hosting: the
response_status column added for this release was nullable, which made every log read fail on requests captured before the upgrade. It is now NOT NULL DEFAULT 0, and a follow-up migration repairs an instance that already applied the first version.
2026-09-18 — Response bodies that can reference themselves
Added
- New
{{mock.url}} token expands to the mock's own base URL — https://quickmock.dev/m/<slug> — so a response body can point at itself. That was impossible before: the slug only exists once the mock is created, and a template author has no way to write it in advance.
- New
{{request.host}} echo token returns the Host the request was addressed to. Go keeps that value outside the header map, so {{request.header.host}} could never stand in for it.
Changed
2026-09-08 — Long-form guides and template depth
Added
- Three guides — mock a REST API endpoint, test a webhook receiver and test retry and back-off logic — are now full write-ups instead of a code snippet with a caption: a direct answer up front, headings that ask the question you arrived with, a section on what usually goes wrong, and an FAQ.
- Every template page now opens with a plain answer to what the template is for and what it deliberately does not do, and ends with links to the vendor documentation or RFC it reproduces — Stripe, Slack, GitHub, Shopify, Telegram, RFC 6749, RFC 7517, RFC 9457 — instead of naming them in passing.
- Guide and template pages carry
FAQPage markup, and a template's markup cites the sources it is built from.
Changed
- Guide and template pages now show when they were last updated, and publish the same date as
dateModified.
- The guides on retry logic and webhook receivers gained sections on when to stop retrying and on building an idempotency key from a delivery ID.
2026-09-08 — Internal linking and hub metadata
Added
- Every guide and template page now ends with links to related pages, and a guide links back to the templates built on it — the two sections are cross-linked in both directions instead of hanging off their index alone.
- The home page links straight into five guides and five templates instead of pointing at the index alone.
- The guides, docs, and changelog pages now carry schema.org markup — an
ItemList, a TechArticle, and a SoftwareApplication with release notes and version.
Changed
- The guides and templates index pages lead their titles with what they actually list, instead of a generic section name.
/llms.txt gained a Documentation section, so the API reference, the OpenAPI contract, and the changelog are grouped where an AI assistant looks for them instead of sitting in the middle of a facts list.
2026-09-05 — Trusted client IP and relative-only redirects
Security
- The client IP behind the per-IP mock limit, the IP blocklist, and the spam-filter allowlist is no longer read from a self-reported
X-Forwarded-For header. It now comes from the immediate reverse proxy's own header (CF-Connecting-IP in production), and only when that peer is trusted — comma-separated forgeries are ignored outright.
- Breaking change: a mock's
Location response header must now be a plain relative path (e.g. /m/abc123) — checked on the main response, named variants, routes, sequence steps, and the error response alike. Absolute URLs, protocol-relative //host, and backslash variants are rejected with 422 validation_failed on create and update; an existing row that still carries one has the header dropped on serve instead.
- Mock responses served from
/m/:slug now send Cross-Origin-Resource-Policy: same-origin, so a mock can no longer be loaded as a script, image, or iframe from another origin. A mock cannot override this header itself.
- The built-in anti-spam filter gained patterns for flag-file reads, secret-bearing environment lookups, loader and reverse-shell one-liners, cloud metadata paths, and two browser-hijacking idioms.
Changed
- The admin token is now required on every mutating call — edit, delete, extend, and log export or clear — with no exception left for mocks created before tokens existed.
2026-08-22 — Go 1.27 and CI refresh
Changed
- The backend and production image now build with Go 1.27.0; the full race-tested suite continues to pass on the new toolchain.
- GitHub Actions now uses Node.js 24-compatible checkout and setup-go releases, with the Go version sourced directly from
go.mod to keep CI and production aligned.
2026-08-19 — Deterministic scenarios, API workspaces and private logs
Added
- Named response variants can now be selected explicitly with
X-Quickmock-Variant or __quickmock_variant, making exact edge cases reproducible on demand.
- Ordered conditional rules select variants from the request method, path, query, headers, or JSON body using
equals, not_equals, contains, and exists operators.
- One mock can now expose up to 50 method-and-path routes. Paste an OpenAPI 3.x JSON or YAML document into the builder to generate the workspace routes and example responses.
- New API documentation and a machine-readable OpenAPI 3.1 contract cover creation, variants, rules, workspaces, and inspector privacy.
Security
- Request logs are private by default for new mocks. The admin token unlocks a path-scoped HttpOnly inspector session; creators can also disable storage of request bodies or sender IPs.
Changed
- SEO/GEO metadata now publishes reciprocal language sitemap entries, includes the documentation page and updated freshness signals, and marks mock, workspace, sharing, and inspector URLs as non-indexable.
2026-08-15 — Abuse resistance and safer mock responses
Security
- The request inspector's fallback polling endpoints now have their own per-IP budget, limiting automated bursts without affecting normal live updates.
- Mock responses can no longer widen a service worker's scope, and redirect responses accept only relative, HTTP, or HTTPS destinations.
- The built-in abuse filter now rejects common SSRF primitives, executable handler configurations, and known backlink-page fingerprints. Self-hosters can also configure an IP/CIDR denylist.
Fixed
- Container logs now rotate by default, and graceful shutdown no longer attempts to persist empty request-log records while draining a closed queue.
2026-08-10 — Hardened mock creation from the browser
Security
- Creating a mock from the site — the form on the home page and the one-click button on a template page — is now rejected when another site triggered it in your browser. Scripted access is unaffected:
curl, CI jobs, and the public API send no browser origin headers and keep working exactly as before.
- Those same two actions now have their own rate-limit budget, separate from the one that serves mock traffic, so a runaway script can't spend your quota of active mocks.
Changed
- The header no longer overflows on a phone: the Templates section made six links too many for one line, so the GitHub link moved down to the footer next to the other author links, and the remaining navigation wraps onto its own row instead of running off the screen.
- Running your own instance: the server now warns at startup about environment variables left over from the old
MOCKAPI_ prefix. Nothing has read them since the rename, so until now a stale key silently left the default in place — including QUICKMOCK_BASE_URL, which the new origin check depends on.
2026-08-09 — Mock template gallery
Added
- New /templates section: ten ready-made mock configurations across four categories (payments, developer tools, auth & identity, general purpose) — Stripe and Shopify webhooks, GitHub push events, Slack's
url_verification handshake, OAuth2/OIDC discovery and JWKS, RFC 9457 error bodies, and more.
- Every template's "Create this mock" button creates it in one click, and the "Open it in the mock builder to tweak first" link opens the same config pre-filled in the regular mock form so you can adjust fields, headers, or status codes before saving.
- Each template page breaks down the payload's key fields and lists exactly how the mock differs from the real service it imitates — no pretending a static mock is the genuine API.
Fixed
- The home page's "you've hit the active-mock limit" banner used to show the request-body size limit instead of the actual mock-count limit, plus a stray
%!(EXTRA int=…) tail. It now shows the correct number with nothing extra appended.
2026-07-26 — TTL cap, mock extension, log export
Added
- A mock's lifetime is now capped:
ttl_seconds can't exceed 30 days (server-configurable), whether set at creation or later.
- The mock page grows an "Extend" button that pushes the expiry further out, up to that same 30-day cap from the mock's creation date — it requires the admin token, same as editing or deleting the mock.
- Response body templates gained four tokens:
{{random.pick:a|b|c}} picks one option per hit, {{seq}} counts hits, plus {{faker.price}} and {{faker.lorem}}.
- The request inspector can filter by HTTP method, and its "Download JSON" link now exports the full captured request/response history (including sender IPs) — requires the admin token, unlike the config export.
2026-07-17 — Admin token per mock
Added
- Creating a mock now returns a one-time admin token, shown once in a copyable banner on the mock page — save it, because it can't be shown again.
- Editing a mock, deleting it, or clearing its logs now needs that token: send it as
Authorization: Bearer <token>. Reading a mock, its logs, and the live inspector still work by slug alone, no token required.
- Mocks created before this release keep working without a token until they expire. New guide: Manage mocks from any device.
2026-07-05 — Live inspector, export & import, anti-spam
Added
- The request inspector streams over Server-Sent Events: new requests appear instantly, with no polling, and the page falls back to polling automatically where SSE is unavailable.
- Export a mock's config as JSON from its page (no logs, no IPs) and import it on the create form — from a file or pasted text, entirely in your browser. The format matches
POST /api/mocks, so exports are replayable via the API.
- An anti-spam content filter blocks abusive mock creation. Operators can replace the built-in pattern list (
QUICKMOCK_SPAM_PATTERNS_FILE), disable it, or allowlist IPs (QUICKMOCK_SPAM_ALLOW_IPS).
Added
- One checkbox on the create form turns on a permissive, credential-free CORS preset (
Access-Control-Allow-Origin: * and friends) and answers OPTIONS preflight with 204 — call a mock from browser JS on any origin, no proxy.
- New use-case guide: Mock an API with CORS.
2026-06-13 — Use-case guides
Added
- New /guide section: scenario-first landing pages for common tasks — mock a REST API, test retry logic, simulate flaky or slow APIs, inspect webhooks, return fake data, echo the request. Each page has a ready-to-run curl example; all are listed in the sitemap.
- Each guide now has a copy button on its curl examples and a one-click Create a mock CTA that opens the home form pre-filled with that recipe's config — method, body, delay, error rate, sequence — ready to submit.
2026-06-11 — Flaky-API simulation
Added
- Response sequences: a mock can now hold an ordered list of up to 10 extra responses (status + body + headers each) cycled per hit — 1st call → main response, 2nd → step 2, … then loop. Classic retry-logic testing. The position is a shared per-mock counter (Redis, with an in-process fallback), so every caller advances the same sequence.
- Error rate & delay jitter: a configurable share of requests (0–100%, rolled per request) can answer with an alternate status/body, and the response delay can be a min–max range for random latency jitter instead of a fixed value. All three settings combine, and a new
X-Mockapi-Variant response header (default, error, or seq-i/n) shows which branch served each hit. Configurable from the create form (new “Flaky simulation” section) and the API.
- The “Who built this” section on the home page now links to the maintainer's personal site (deadsquirrel.dev), language-matched per locale.
2026-06-10 — Request echo tokens
Added
- New
request.* dynamic tokens echo the incoming request back into the response body: {{request.method}}, {{request.path}}, {{request.ip}}, {{request.query.id}}, {{request.header.x-request-id}}, {{request.body}}, and JSON dot paths like {{request.body.user.name}}. Values are inserted verbatim and nothing extra is stored — a static mock can now answer with data from the request itself.
Fixed
- The 2026-05-20 entry below used to display Go-template escaping (
{{`…`}}) around every token instead of the token itself — leftover from copying the markup out of an HTML template. Now it shows plain {{faker.uuid}} and friends.
Added
- Footer credit now links the author name to the maintainer's personal site (deadsquirrel.dev), language-matched per locale, with
rel="me" as an identity signal.
2026-05-25 — Site content pass
Added
- This /changelog page itself, in EN and RU, with hreflang and an entry in
sitemap.xml.
- Home page: a Quick glossary section (Mock API, Endpoint, TTL, Faker token, Content-Type) for readers landing here without context.
- Home page: a Who built this block with author bio and direct links to GitHub, LinkedIn, Telegram — and a Why I built Quickmock personal-story section, so the page isn't a faceless tool landing.
- Visible Last updated stamp at the bottom of the home page;
dateModified mirrored into the WebApplication JSON-LD so AI crawlers see freshness.
Fixed
?lang=<code> in the URL now actually serves that language. Previously the canonical / hreflang links pointed at ?lang=ru but the resolver only read the cookie and Accept-Language, so a bot following the hreflang link saw English content — the multilingual signal was a lie.
Security
- Origin hardening for
/m/*: the public mock router now strips request-altering headers a malicious caller could use to confuse downstream proxies, slugs were lengthened (more entropy), and internal error messages are redacted before they reach the wire — no DSN fragments, no stack frames.
- Language switcher (
POST /language) now accepts same-origin absolute Referers, so the redirect-back path works behind reverse proxies that rewrite the Referer header. Cross-origin Referers are still rejected.
Fixed
- RealIP middleware now trusts
X-Forwarded-For / X-Real-IP when the immediate peer is loopback or a private/link-local address. Previously XFF was honored only for loopback peers, so behind Docker Compose every request was attributed to the bridge gateway (e.g. 172.18.0.1) — making creator_ip and request_ip useless and turning the per-IP mock limit into a global one.
- Mock router now stores incoming request headers with lowercased keys, so
request_headers->>'user-agent' works regardless of wire casing.
- UI: slug examples updated to the new 12-char format; Copy-URL button restored on the mock detail page.
2026-05-21 — UGC disclaimer
Added
- Footer disclaimer and Terms-of-use modal: mocks are user-generated content, prohibited categories (malware, phishing, CSAM, attack infrastructure) are spelled out, and an abuse-report channel is published.
- Footer modal stays usable on small viewports — close button is no longer clipped on mobile.
2026-05-20 — Dynamic tokens
Added
- Faker / now token substitution in response bodies:
{{faker.uuid}}, {{faker.email}}, {{faker.name}}, {{now.iso8601}}, plus 18 more (people, IDs, network, text, time). The mock stores the template as-is and renders fresh random values on every hit, so one mock can serve different data per call. Full list lives under Dynamic tokens on the create form.
2026-05-18 — Hardening & quality of life
Fixed
- Docker Compose deploy now waits for the
--wait healthcheck instead of polling /healthz in a shell loop, so deploys no longer print the cosmetic curl: (52) Empty reply while the proxy and Go process finish racing.
POSTGRES_PASSWORD example in .env.example now warns against base64-encoded values — pgx's DSN parser chokes on + / = in the password.
2026-05-17 — Initial public release
Added
- Core mock engine: create, view, edit, delete; all HTTP methods including
ANY.
- Public
/m/:slug router with configurable status, body, headers, response delay (0–30 s), and TTL.
- Live request inspector: HTMX 2-second poll, per-mock counter, clear-logs.
- cURL import — paste a
curl … command to pre-fill the create form.
- Code snippets on the mock detail page for JavaScript, Python, Go, and PHP.
- Read-only share preview at
/share/:slug with Open Graph tags.
- Per-IP rate limit (5000 hits / 8 hours) and per-IP active-mock cap (50).
- 512 KB max response body, enforced by handler and a DB
CHECK constraint.
- Docker Compose stack (app + Postgres + Redis + one-shot migration container) and a bare-metal systemd path.
- EN + RU localization, language switcher (cookie + Accept-Language).
- Privacy by design: no third-party JS, no analytics, no fonts CDN; CI enforces this on every build.
Full git history on GitHub →
← Back to home