Map Rules
Map Rules let you modify traffic behavior without touching the target server. Want to serve a local JSON file instead of hitting the real API? Map Local. Want to redirect production API calls to your staging server? Map Remote. Want to change a header value or swap text in response bodies? Rewrite. All three work transparently — your application doesn’t know Ghost is modifying traffic.
This is powerful for testing: you can simulate API responses that don’t exist yet, test against different environments, inject specific error responses, or modify response data to test how your app handles edge cases — all without any code changes or server access.
Rule Types
Section titled “Rule Types”Ghost supports three types of map rules, each serving a different purpose:
What this diagram shows: Every incoming request is checked against your map rules in priority order. If a rule matches, Ghost applies the corresponding action — serving a local response, redirecting to another server, or applying text replacements. If no rule matches, the request passes through normally to its original destination. Each matched flow gets tagged so you can see which rules were applied.
Map Local
Section titled “Map Local”Serves a synthetic response without contacting the upstream server at all. The request never leaves Ghost — it’s answered locally.
Configuration fields:
| Field | Description |
|---|---|
| Status Code | The HTTP status code to return (default: 200). Set to 404 or 500 to simulate error responses. |
| Content Type | The response Content-Type header. Auto-detected from file extension if not set (e.g., .json → application/json, .html → text/html, .png → image/png). Falls back to Go’s http.DetectContentType for unknown extensions. |
| File Path | Path to a local file to serve as the response body. Maximum file size: 50 MB. The file must be under the ~/.ghost/ directory (path traversal with .. is blocked for security). File path takes priority over inline body — if both are set, the file is used. |
| Body | Inline response body text. Used only when no file path is set or the file can’t be read. |
The response also includes an X-Ghost-Rule header set to the rule name, so you can verify in the response headers which rule was applied.
Content type auto-detection recognizes these file extensions: .json, .xml, .html, .htm, .css, .js, .svg, .png, .jpg, .jpeg, .gif, .webp, .txt. For other extensions, Ghost sniffs the first few bytes of the file content to detect the type.
Use cases:
- Mock an API that doesn’t exist yet — design the frontend before the backend is ready
- Simulate errors — return a 503 response to test how your app handles server downtime
- Serve local assets — develop UI changes by serving a local CSS or JavaScript file instead of the remote version
- Test edge cases — return an empty array, a null field, or an extremely large response to test boundary conditions
Map Remote
Section titled “Map Remote”Rewrites the request URL to redirect traffic to a different server while preserving the original path and query string. The client doesn’t know the request was rerouted — it sees the same URL it requested.
| Original Request | Target | Resulting Request |
|---|---|---|
https://api.prod.com/v1/users?page=2 | https://api.staging.com | https://api.staging.com/v1/users?page=2 |
https://cdn.example.com/images/logo.png | http://localhost:3000 | http://localhost:3000/images/logo.png |
How it works: Ghost takes the target URL you specify, extracts just the scheme (http:// or https://) and host, and replaces the original request’s scheme and host — keeping the path, query parameters, and everything else intact. The Host header is also updated to match the new target.
Use cases:
- Route to staging — redirect production API calls to a staging server to test integration
- Test against localhost — point mobile app traffic to your local development server
- Switch API versions — redirect from one API host to another without changing the client code
- Load balancing testing — redirect traffic to a specific backend server
Rewrite
Section titled “Rewrite”Applies regex find-and-replace on request or response data. Unlike Map Local and Map Remote which change the destination, Rewrite modifies the content while the request still goes to the original server.
Configuration fields:
| Field | Description |
|---|---|
| Phase | When to apply the replacement: request (before the request is sent to the server) or response (after the response comes back) |
| Target | What to rewrite: url (the request URL), body (the request or response body depending on phase), or header:<name> (a specific header value, e.g., header:Authorization) |
| Find | The search pattern. Supports two modes: plain text (exact string match — special regex characters are auto-escaped) or regex (full Go regular expression syntax). Toggle between modes in the UI. |
| Replace | The replacement string. Supports backreferences ($1, $2, etc.) that insert captured groups from the regex pattern. |
Examples:
| Phase | Target | Find | Replace | What It Does |
|---|---|---|---|---|
| request | url | /v1/ | /v2/ | Upgrades API version in the URL |
| request | header:Authorization | Bearer old-token | Bearer new-token | Swaps the auth token on every request |
| response | body | "debug":\s*true | "debug": false | Strips debug flags from JSON responses |
| request | header:Accept-Language | en | tr | Changes the language preference to Turkish |
| response | body | "price":(\d+) | "price":0 | Sets all prices to zero for testing free-tier display |
When rewriting headers, Ghost iterates through all values of the named header (HTTP headers can have multiple values), applies the regex replacement to each, and updates the header. When rewriting the body, the Content-Length is automatically updated to reflect the new size.
The regex engine is Go’s standard regexp package — the compiled regex is cached per rule (thread-safe with a mutex) for performance, and invalidated when the pattern changes.
The UI includes a “Test & Edit” feature for regex mode — type your pattern and replacement, and see the result before the rule is applied to live traffic.
Rule Matching
Section titled “Rule Matching”Rules match incoming requests using three criteria — all three must match for a rule to apply:
| Criteria | Match Type | Pattern Syntax | Example Pattern | What It Matches |
|---|---|---|---|---|
| Host | Glob (path.Match) | * matches any sequence of characters within a path segment, ? matches any single character, [abc] matches character classes | *.example.com | api.example.com, cdn.example.com, www.example.com |
| Path | Glob (path.Match) | Same glob syntax as host | /api/v1/* | /api/v1/users, /api/v1/products, /api/v1/orders |
| Method | Exact (case-insensitive) | HTTP method or empty for “any” | POST | Only POST requests. Leave empty to match all methods. |
Empty patterns match everything. If you leave the host pattern empty, the rule matches any host. Leave all three empty and the rule matches all traffic.
Important: The * glob only matches within a single path segment (separated by /). To match nested paths, you’d need multiple segments: /api/*/users/* matches /api/v1/users/123 but /api/* only matches /api/anything (one level deep, not /api/v1/users).
Priority and Ordering
Section titled “Priority and Ordering”Every rule has a priority number (integer). Lower numbers run first.
The behavior differs by rule type:
| Rule Type | Evaluation Strategy | What It Means |
|---|---|---|
| Map Local | First match wins (short-circuit) | Ghost checks enabled Map Local rules in priority order and uses the first one that matches. Other Map Local rules for the same request are skipped. |
| Map Remote | First match wins (short-circuit) | Same as Map Local — the first matching enabled remote rule is used. |
| Rewrite | All matches apply (cumulative) | Every matching enabled rewrite rule is applied in priority order. Multiple rewrite rules can modify the same request/response. |
When priorities are equal, rules are sorted by their insertion order (the order they were created).
Pipeline Position
Section titled “Pipeline Position”Map Rules run early in Ghost’s interceptor pipeline — second in the chain:
Breakpoints → Map Rules → Addons → Storage → SecurityThis order matters:
- Map Local short-circuits the pipeline for response — the proxy never contacts the upstream server. The synthetic response still passes through downstream interceptors (Addons, Storage, Security), so addons can tag it and it gets stored in the database.
- Map Remote rewrites the URL before the proxy connects to the upstream, so the connection goes to the new target.
- Rewrite modifications on the request happen before addons and storage see it, so modified URLs and bodies are what get recorded.
- Breakpoints run before Map Rules, so you can pause and inspect a request before any map rule applies.
Flow Metadata
Section titled “Flow Metadata”When a rule matches, Ghost adds metadata to the flow so you can see what happened:
| Tag | Meaning |
|---|---|
map-local | The response was served locally (the request never reached the real server) |
map-remote | The request URL was rewritten to a different target server |
rewrite | A regex replacement was applied to the request or response |
Additional metadata fields:
map_rule— the name of the rule that matched (for Map Local and Map Remote)map_remote_target— the redirect destination URL (for Map Remote)rewrite_rule— the name of the rewrite rule (for Rewrite)
These tags are searchable via GQL: tag:map-local shows all flows served locally, tag:rewrite shows all modified flows.
Rules Panel UI
Section titled “Rules Panel UI”The rules panel opens as a slide-over from the toolbar (Ctrl+3). It has two views:
List View
Section titled “List View”Shows all map rules as cards, each displaying:
- Type icon — color-coded: green for Local, cyan for Remote, purple for Rewrite
- Rule name — the display label you gave the rule
- Type badge — “Local”, “Remote”, or “Rewrite”
- Pattern display — shows the method, host, and path patterns (e.g., “POST .api.com /checkout/”)
- Hover actions — toggle enable/disable and delete button
An “Add” button in the header creates a new rule.
Editor View
Section titled “Editor View”When you click a rule or create a new one, a step-by-step editor opens:
Step 1: “What should Ghost do?” — Select the rule type by clicking one of three cards:
- Map Local (green) — “Serve a local response”
- Map Remote (cyan) — “Redirect to another server”
- Rewrite (purple) — “Find and replace content”
Step 2: “Match requests where…” — Configure when this rule should activate:
- Rule name (for your reference)
- Host pattern (glob)
- Path pattern (glob)
- Method (dropdown: Any, GET, POST, PUT, DELETE, PATCH)
- Priority number
Step 3: Type-specific configuration (varies by rule type — see the fields described above for each type).
Actions footer: Enable/disable toggle, Cancel, and Save buttons.
Persistence
Section titled “Persistence”Map rules are stored in SQLite (map_rules table with 17 data fields). Changes are applied immediately to the in-memory rule manager — no proxy restart needed. The rule set is reloaded from the database on Ghost startup.
WebSocket Events
Section titled “WebSocket Events”Rule changes are broadcast to all connected frontends in real-time:
| Event | When It Fires |
|---|---|
rule.created | A new rule was saved — includes the full rule data |
rule.updated | A rule was modified or toggled — includes the updated rule data |
rule.deleted | A rule was removed — includes only the rule ID |
API Reference
Section titled “API Reference”| Method | Endpoint | Description |
|---|---|---|
GET | /api/v1/rules | List all map rules, sorted by priority |
POST | /api/v1/rules | Create a new rule |
GET | /api/v1/rules/{id} | Get a single rule by ID |
PUT | /api/v1/rules/{id} | Update a rule (name, patterns, type-specific fields) |
DELETE | /api/v1/rules/{id} | Delete a rule |
POST | /api/v1/rules/{id}/toggle | Toggle a rule’s enabled state without changing anything else |