Skip to content

Built-in CORS

A browser calling a Mimic-backed API from http://localhost:3000 normally fails twice: it sends OPTIONS /users first (which no mock answers), and every real mock has to repeat Access-Control-Allow-Origin in its response_headers. For an API with N endpoints that’s up to 2N files maintained by hand.

One variable replaces all of them:

Terminal window
MIMIC_CORS=true

That’s it — preflights are answered automatically and every mock response carries the allow-origin header.

Variable Default What it does
MIMIC_CORS false Master switch. Everything below is ignored while it’s off.
MIMIC_CORS_ORIGINS * *, or a comma-separated allowlist like http://localhost:3000,http://localhost:5173.
MIMIC_CORS_METHODS GET,POST,PUT,PATCH,DELETE,OPTIONS Advertised as Access-Control-Allow-Methods on a preflight.
MIMIC_CORS_HEADERS * * reflects the request’s Access-Control-Request-Headers; a list is sent verbatim.
MIMIC_CORS_CREDENTIALS false Sends Access-Control-Allow-Credentials: true.
MIMIC_CORS_MAX_AGE 600 Access-Control-Max-Age, in seconds.
Terminal window
docker run -d -p 8080:8080 \
-e MIMIC_CORS=true \
-e MIMIC_CORS_ORIGINS=http://localhost:3000,http://localhost:5173 \
-v $(pwd)/mocks:/app/mocks:ro \
ragilhadi/mimic:latest
Terminal window
# No OPTIONS mock anywhere — Mimic answers the preflight itself
curl -i -X OPTIONS http://localhost:8080/users \
-H 'Origin: http://localhost:3000' \
-H 'Access-Control-Request-Method: POST' \
-H 'Access-Control-Request-Headers: content-type'
HTTP/1.1 204 No Content
access-control-allow-origin: *
access-control-allow-methods: GET, POST, PUT, PATCH, DELETE, OPTIONS
access-control-allow-headers: content-type
access-control-max-age: 600
  • Off by default. With MIMIC_CORS unset, responses are byte-identical to what they were before this existed — no new headers, and OPTIONS still 404s.
  • A mock’s own header always wins. If a mock sets Access-Control-Allow-Origin in its response_headers, the global config leaves it alone — so mocking a CORS failure stays possible.
  • Preflights are gap-filled, not hijacked. An OPTIONS request that no mock matches, but whose path has a mock registered for the method in Access-Control-Request-Method, is answered 204. An explicit OPTIONS mock matches first and wins. A preflight for a path with nothing behind it still 404s.
  • Only real endpoints, in the current scenario. A mock hidden by the active scenario has no endpoint to preflight, and its preflight 404s too.
  • A bare OPTIONS isn’t a preflight. Without Access-Control-Request-Method — curl, a health probe — the request behaves exactly as it always has.
  • Preflights are logged. They appear in /admin/requests and the dashboard with matched_mock: null and the explanation “answered as a CORS preflight”, so nothing looks dropped.
  • The allowlist is honored. A request from an origin outside MIMIC_CORS_ORIGINS gets no Access-Control-Allow-Origin header rather than a wrong one, which is what makes the browser’s own error message correct. Responses that depend on the origin also carry Vary: Origin.
  • * with credentials. MIMIC_CORS_ORIGINS=* plus MIMIC_CORS_CREDENTIALS=true is invalid per the CORS spec; Mimic reflects the request origin instead and warns once at startup.
  • Matching is untouched: CORS headers are added to the response, never to the request the matcher sees.
  • Mimic’s own error responses carry the headers too. The mock not found 404, the payload too large 413, the admin API’s 401, and both proxy fallback outcomes all get the same Access-Control-Allow-Origin (and friends) a matched mock would — so a request to a path you forgot to mock reads as a 404 in the browser console, not as an opaque CORS failure. A proxied response that already carries its own Access-Control-Allow-Origin from upstream keeps it.
Terminal window
docker run -d -p 8080:8080 \
-e MIMIC_CORS=true \
-e MIMIC_CORS_ORIGINS=http://localhost:3000 \
-v ./mocks:/app/mocks:ro ragilhadi/mimic:latest

Point your frontend dev server at http://localhost:8080 — preflights are handled for you, and every JSON response already carries the right allow-origin header.