{"id":32653270,"url":"https://github.com/tomsh-hr/caddy-geojs-blocker","last_synced_at":"2026-06-30T06:31:40.545Z","repository":{"id":319654094,"uuid":"1079224788","full_name":"tomsh-hr/caddy-geojs-blocker","owner":"tomsh-hr","description":"Caddy v2 module for geoblocking or geowhitelisting requests using the free GeoJS API — with caching, stats, and debug endpoint.","archived":false,"fork":false,"pushed_at":"2025-10-19T18:28:28.000Z","size":44,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-10-31T08:26:50.162Z","etag":null,"topics":["caddy","caddy-module","caddyserver","geoblocking","geoip","geojs","geowhitelisting"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tomsh-hr.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-10-19T11:31:24.000Z","updated_at":"2025-10-19T14:38:53.000Z","dependencies_parsed_at":"2025-10-19T21:35:22.114Z","dependency_job_id":null,"html_url":"https://github.com/tomsh-hr/caddy-geojs-blocker","commit_stats":null,"previous_names":["tomsh-hr/caddy-geojs-blocker"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/tomsh-hr/caddy-geojs-blocker","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tomsh-hr%2Fcaddy-geojs-blocker","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tomsh-hr%2Fcaddy-geojs-blocker/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tomsh-hr%2Fcaddy-geojs-blocker/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tomsh-hr%2Fcaddy-geojs-blocker/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tomsh-hr","download_url":"https://codeload.github.com/tomsh-hr/caddy-geojs-blocker/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tomsh-hr%2Fcaddy-geojs-blocker/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34955985,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-30T02:00:05.919Z","response_time":92,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["caddy","caddy-module","caddyserver","geoblocking","geoip","geojs","geowhitelisting"],"created_at":"2025-10-31T08:17:57.444Z","updated_at":"2026-06-30T06:31:40.521Z","avatar_url":"https://github.com/tomsh-hr.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Caddy GeoJS Blocker Module\n![Go CI](https://github.com/tomsh-hr/caddy-geojs-blocker/actions/workflows/go.yml/badge.svg)\n\nA lightweight **Caddy v2 HTTP handler module** for **geoblocking** or **geowhitelisting** requests based on client IP country codes, using the free [GeoJS API](https://www.geojs.io/).  \nSupports both **allowlisting** (default allow, block others) and **blocklisting** (default allow, block specific).  \nIncludes per-instance caching, concurrency-safe stats, and an optional debug endpoint for monitoring.\n\n---\n\n## ✨ Features\n\n- **GeoIP Lookup:** Fetches 2-letter country code (e.g., `DE`) from GeoJS.  \n- **Caching:** In-memory LRU-like cache (default 50k entries, 15m TTL) to minimize API calls.  \n- **Concurrency:** Optional `singleflight` deduplication for simultaneous lookups.  \n- **Stats:** Atomic counters for allowed/blocked requests with JSON export.  \n- **Log Vars:** Sets `geojs_country` and `geojs_decision` for access logs.  \n- **Debug Endpoint:** Optional `/debug/geojs` endpoint for stats snapshot/reset (token-protected).\n\n---\n\n## ⚙️ Installation\n\n### Build Caddy with the Module\n\nBest way is to use **xcaddy**:\n\n```bash\nxcaddy build --with github.com/tomsh-hr/caddy-geojs-blocker\n```\n\nor:\n\n```bash\n# Clone Caddy\ngit clone https://github.com/caddyserver/caddy.git \u0026\u0026 cd caddy\n\n# Add the module import\n# In cmd/caddy/main.go:\nimport _ \"github.com/tomsh-hr/caddy-geojs-blocker\"\n\ngo mod tidy\n\n# Build Caddy\ngo build \\\n  -tags 'nobadger,nomysql,nopgx' \\\n  -trimpath \\\n  -ldflags '-w -s' \\\n  -o ./caddy ./cmd/caddy\n```\n\nThen run:\n\n```bash\n./caddy run --config Caddyfile\n```\n\n---\n\n## 🧩 Usage\n\nPlace the directive in a `route` block.\n\n- Use **`geojs_allow`** for allowlisting (only specific countries allowed).  \n- Use **`geojs_block`** for blocklisting (specific countries blocked).  \n\nInline arguments are ISO2 country codes; options are set within the block.\n\n---\n\n### ✅ Allow Only Specific Countries\n\n```caddyfile\n:80 {\n  route {\n    geojs_allow DE US RU CN {\n      cache_ttl 30m\n      debug_path /debug/geojs\n    }\n    respond \"Hello from {http.vars.geojs_country}!\"\n  }\n}\n```\n\n→ Allows Germany (`DE`), United States (`US`), Russia (`RU`), and China (`CN`); blocks all others with `403`.\n\n---\n\n### 🚫 Block Specific Countries\n\n```caddyfile\n:80 {\n  route {\n    geojs_block DE US RU CN\n    respond \"Access granted from {http.vars.geojs_country}!\"\n  }\n}\n```\n\n→ Blocks Germany (`DE`), United States (`US`), Russia (`RU`), and China (`CN`); allows all others.\n\n---\n\n### 🧾 Full Example with All Options\n\n```caddyfile\n:80 {\n  log {\n    format json\n  }\n\n  route {\n    geojs_allow DE US RU CN {\n      cache_ttl 10m\n      cache_size 10000\n      singleflight off\n      allow_undetected off\n      prune_interval 2m\n      debug_path /debug/geojs\n      debug_token mysecret\n    }\n\n    respond \"Welcome from {http.vars.geojs_country} ({http.vars.geojs_decision})\"\n  }\n}\n```\n\n---\n\n### Directive placement \u0026 order\n\n`geojs_allow` and `geojs_block` are HTTP handlers. Use them inside a `route` block, or configure global ordering if you prefer global (site-wide) geoblocking.\n\n**Preferred:**\n\n```caddyfile\n:80 {\n  route {\n    geojs_allow DE US RU CN\n    respond \"Hello from {http.vars.geojs_country}!\"\n  }\n}\n```\n\n**Alternative with global order:**\n\n```caddyfile\n{\n  order geojs_allow before respond\n  order geojs_block before respond\n}\n:80 {\n  geojs_allow DE US RU CN\n  respond \"Hello from {http.vars.geojs_country}!\"\n}\n```\n\n---\n\n## 🔧 Options\n\n| Option | Type | Description | Example | Default |\n|--------|------|-------------|----------|----------|\n| **Country Codes (inline)** | `[]string` | ISO2 codes for allow/block list | `geojs_allow DE US RU CN` | — |\n| **cache_ttl** | Duration | Cache TTL for IP lookups | `cache_ttl 30m` | `15m` |\n| **cache_size** | int | Max cache entries | `cache_size 10000` | `50000` |\n| **singleflight** | string | Deduplicate concurrent lookups (`on/off`) | `singleflight off` | `on` |\n| **allow_undetected** | string | Allow (`on`) or block (`off`) undetected IPs (lookup failures, etc.) | `allow_undetected off` | `on` |\n| **prune_interval** | Duration | Prune expired cache entries | `prune_interval 1h` | `5m` |\n| **debug_path** | string | Path for stats JSON endpoint | `debug_path /debug/geojs` | *(disabled)* |\n| **debug_token** | string | Token for debug auth (header `X-Debug-Token`) | `debug_token mysecret` | *(none)* |\n\n---\n\n## ⚙️ Recommended Settings by Use Case\n\nDifferent environments benefit from different cache and pruning settings.  \nHere are suggested values you can tune to balance performance and accuracy.\n\n| Scenario | Description | cache_ttl | cache_size | prune_interval | singleflight | Notes |\n|-----------|--------------|------------|-------------|----------------|---------------|-------|\n| 🏠 **Home Server / Personal Use** | Few visitors, low load | `30m` | `1000` | `10m` | `on` | Keeps memory use minimal and still avoids repeat lookups. |\n| 🚀 **Low-Traffic Site** | Small business, blog, small API | `1h` | `10000` | `10m` | `on` | Reduces API requests while maintaining fresh lookups. |\n| 🌍 **High-Traffic / Production** | Many visitors or global access | `6h` | `50000–100000` | `15m` | `on` | Minimizes API hits, faster response, more RAM usage. |\n| 🧪 **Debug / Testing** | Development, frequent restarts | `5m` | `1000` | `1m` | `off` | More frequent lookups help see live GeoJS behavior. |\n\n💡 **Tip:**  \nFor very high traffic, keep `singleflight on` — it prevents multiple concurrent lookups for the same IP and saves both time and API requests.\n\n---\n\n## 🐛 Debug Endpoint\n\nIf `debug_path` is set (e.g., `/debug/geojs`):\n\n**GET** → Returns JSON stats:\n\n```json\n{\n  \"total_allowed\": 4,\n  \"total_blocked\": 0,\n  \"allowed_by_cc\": { \"DE\": 2, \"US\": 1, \"RU\": 1, \"CN\": 0 },\n  \"blocked_by_cc\": {}\n}\n```\n\n**POST ?reset=1** → Resets counters.  \n**Example:**\n```bash\ncurl http://localhost:80/debug/geojs\ncurl -X POST http://localhost:80/debug/geojs?reset=1\n```\n\nRequires header `X-Debug-Token: {token}` if `debug_token` is set.  \n**Example with token:**\n```bash\ncurl -H \"X-Debug-Token: mysecret\" http://localhost:80/debug/geojs\ncurl -X POST -H \"X-Debug-Token: mysecret\" http://localhost:80/debug/geojs?reset=1\n```\n\n---\n\n## ⚠️ Notes\n\n- **API Usage:** GeoJS currently has *no fixed rate limits*, but may restrict users who make excessive requests. The in-memory cache and optional singleflight mode help reduce unnecessary lookups.  \n- **IPv6 Support:** Fully supported.  \n- **Proxy Headers:** Prefers `X-Forwarded-For`, then `X-Real-IP`, then `RemoteAddr`.  \n- **Cache Eviction:** Uses simple random eviction when full (not full LRU).  \n- **Validation:** You cannot mix both blocklist and allowlist in one directive.\n\n---\n\n## 🧪 Testing\n\n```bash\n# Test with known IP\ncurl -H \"X-Forwarded-For: 91.64.46.1\" http://localhost:80\n# → DE IP → allowed\n\n# View stats\ncurl http://localhost:80/debug/geojs\n\n# View stats with token\ncurl -H \"X-Debug-Token: mysecret\" http://localhost:80/debug/geojs\n\n# Reset counters (token protected)\ncurl -X POST -H \"X-Debug-Token: mysecret\" http://localhost:80/debug/geojs?reset=1\n```\n\n---\n\n## 🪵 Logging Integration\n\nIf you want GeoJS Blocker’s decision data (`geojs_country`, `geojs_decision`) to appear in your main access logs, you can append them manually to log entries using the following Caddyfile block:\n\n```caddyfile\nlog {\n  format json\n}\n\nhandle_errors {\n  log_append geojs_country  {http.vars.geojs_country}\n  log_append geojs_decision {http.vars.geojs_decision}\n  respond \"{http.error.status_code} {http.error.status_text}\"\n}\n\nroute {\n  geojs_allow DE US RU CN\n  \n  log_append geojs_country  {http.vars.geojs_country}\n  log_append geojs_decision {http.vars.geojs_decision}\n}\n```\n\nThis ensures both normal requests and blocked responses include the GeoJS decision context in your JSON access log output.\n\n---\n\n## 🌍 Supported Country Codes\n\nGeoJS returns [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) country codes.  \nYou can use any of these two-letter codes (case-insensitive) in `geojs_allow` or `geojs_block` directives.  \nThe module automatically converts them to uppercase and ignores invalid entries.\n\n---\n\n## 📜 License\n\nLicensed under the [MIT License](./LICENSE).\n\nContributions welcome!  \nBuilt with ❤️ for [Caddy](https://caddyserver.com).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftomsh-hr%2Fcaddy-geojs-blocker","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftomsh-hr%2Fcaddy-geojs-blocker","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftomsh-hr%2Fcaddy-geojs-blocker/lists"}