osmproxy is a small HTTP tile proxy for slippy-map tiles. It fetches tiles from configured upstream map providers, stores them in an S3-compatible object store, and serves cached responses on later requests.
- S3-compatible tile caching
- Multiple named map origins through
PROXY_MAPS - Origin fallback on cache miss
- Stale cached tile serving with background refresh
- Cache inspection endpoint
- Simple Express HTTP API
flowchart LR
client["Map client"] --> proxy["osmproxy"]
proxy --> allowlist["Map allowlist\nPROXY_MAPS"]
allowlist --> cache{"Tile in S3 cache?"}
cache -- "fresh hit" --> s3["S3-compatible storage"]
s3 --> proxy
cache -- "miss or stale refresh" --> origin["Upstream tile origin"]
origin --> proxy
proxy --> store["Store or refresh cached tile"]
store --> s3
proxy --> client
- Node.js 20 or newer
- An S3-compatible object store
npm install
cp .env.example .env| Variable | Description | Default |
|---|---|---|
PORT |
HTTP server port | 3000 |
AWS_REGION |
S3 region | ap-northeast-2 |
S3_BUCKET |
Bucket used for cached tiles | Required |
S3_ENDPOINT |
Optional S3-compatible endpoint | unset |
S3_FORCE_PATH_STYLE |
Use path-style S3 requests | false |
CACHE_TTL_MS |
Freshness window for cached tiles | 300000 |
PROXY_MAPS |
Comma-separated name=url origin map |
Required |
Example:
PORT=3000
AWS_REGION=ap-northeast-2
S3_BUCKET=tiles
S3_ENDPOINT=
S3_FORCE_PATH_STYLE=false
CACHE_TTL_MS=300000
PROXY_MAPS=base=https://tile.openstreetmap.orgPROXY_MAPS values are exposed as /tiles/:map/... route names. Origin URLs should not include a trailing slash.
Run in development mode:
npm run devBuild and start:
npm run build
npm startRun tests:
npm testGET /healthResponse:
{
"ok": true,
"maps": ["base"]
}GET /tiles/:map/:z/:x/:y.:extExample:
GET /tiles/base/1/2/3.pngTile paths use standard slippy-map order: {z}/{x}/{y}.{ext}.
Responses include an X-Cache header:
| Value | Meaning |
|---|---|
HIT |
Tile was served from fresh cache |
MISS |
Tile was fetched from origin and stored |
STALE |
Stale cached tile was served while refresh runs in the background |
GET /tiles/:map/:z/:x/:y.:ext/infoExample:
GET /tiles/base/1/2/3.png/infoReturns cache metadata when the tile exists in storage. Returns cached: false when the tile is absent.
osmproxy is designed to reduce repeated upstream tile requests by serving cached tiles directly from S3-compatible storage. The largest benefit appears on warm-cache traffic, where the proxy can avoid origin latency and return the stored tile immediately.
Example warm-cache benchmark from one deployment:
| Metric | Cached proxy | Upstream origin | Comparison |
|---|---|---|---|
| Success rate | 100% | 100% | Same |
| Median total time | 21.3 ms | 883.2 ms | Proxy 41.5x faster |
| Mean total time | 22.1 ms | 836.1 ms | Proxy 37.9x faster |
| p95 total time | 29.4 ms | 1103.5 ms | Proxy 37.5x faster |
| Median TTFB | 20.5 ms | 882.4 ms | Proxy 43.0x faster |
| Mean tile size | 20.1 KiB | 20.1 KiB | Same |
Benchmark shape:
- 9 tile coordinates
- 1 warmup pass
- 45 measured requests per endpoint
- Same client network location
- Same tile content and image format
Actual results depend on cache state, S3 provider latency, upstream latency, tile size, and user region. Cold-cache requests include origin fetch and storage write overhead, so they are expected to be slower than warm-cache hits.
This project is licensed under the MIT License.
Map tiles, map data, and rendered assets served through this proxy remain subject to the license, attribution requirements, rate limits, and usage policies of their upstream providers. Configure and operate this proxy only in compliance with those provider terms.