{"id":13983944,"url":"https://github.com/pyed/ipfilter","last_synced_at":"2026-01-22T05:30:54.659Z","repository":{"id":57480705,"uuid":"41841715","full_name":"pyed/ipfilter","owner":"pyed","description":"ipfilter is a middleware for Caddy that blocks or allows requests based on the client's IP","archived":false,"fork":false,"pushed_at":"2019-07-07T12:47:50.000Z","size":1032,"stargazers_count":85,"open_issues_count":5,"forks_count":18,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-08-13T16:58:48.521Z","etag":null,"topics":["caddy","directive","filter","ip","middleware"],"latest_commit_sha":null,"homepage":"https://caddyserver.com/docs/ipfilter","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pyed.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}},"created_at":"2015-09-03T04:21:51.000Z","updated_at":"2025-08-11T21:48:58.000Z","dependencies_parsed_at":"2022-09-26T22:11:34.974Z","dependency_job_id":null,"html_url":"https://github.com/pyed/ipfilter","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/pyed/ipfilter","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pyed%2Fipfilter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pyed%2Fipfilter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pyed%2Fipfilter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pyed%2Fipfilter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pyed","download_url":"https://codeload.github.com/pyed/ipfilter/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pyed%2Fipfilter/sbom","scorecard":{"id":751337,"data":{"date":"2025-08-11","repo":{"name":"github.com/pyed/ipfilter","commit":"44576102099e4428e7e429987a97a45122c4a656"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":2.9,"checks":[{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Code-Review","score":0,"reason":"Found 2/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: Apache License 2.0: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 2 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":9,"reason":"1 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-2927-hv3p-f3vp"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-22T20:28:38.796Z","repository_id":57480705,"created_at":"2025-08-22T20:28:38.797Z","updated_at":"2025-08-22T20:28:38.797Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28656192,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-22T01:17:37.254Z","status":"online","status_checked_at":"2026-01-22T02:00:07.137Z","response_time":144,"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","directive","filter","ip","middleware"],"created_at":"2024-08-09T05:02:02.564Z","updated_at":"2026-01-22T05:30:54.641Z","avatar_url":"https://github.com/pyed.png","language":"Go","funding_links":[],"categories":["Go"],"sub_categories":[],"readme":"# ipfilter\n[![Go Report Card](https://goreportcard.com/badge/pyed/ipfilter)](https://goreportcard.com/report/pyed/ipfilter)\n\nThis is middleware for the [Caddy](http://caddyserver.com)\nweb server that implements black and whitelisting based on\nIP addresses (or CIDR ranges) or country of origin using a\n[MaxMind](https://dev.maxmind.com/geoip/geoip2/geolite2/) database.\n\n## Syntax\n\n```\nipfilter \u003cbasepath\u003e {\n    rule       \u003cblock | allow\u003e\n    ip         \u003caddresses or CIDR ranges to block\u003e\n    prefix_dir \u003cIP addr directory prefix\u003e\n    database   \u003c/path/to/GeoLite2-Country.mmdb\u003e\n    country    \u003cISO two letter country codes\u003e\n    blockpage  \u003cblockpage.html\u003e\n    strict\n}\n```\n\nYou can specify zero or more `ipfilter` blocks. Each `ipfilter` block has\nto specify at least one `ip`, `prefix_dir` or `country` directive. If no\n`ipfilter` blocks are defined this middleware will allow every request.\n\n* **basepath**: A sequence of URI path prefixes to match for the filter\nto be active. You have to specify at least one path prefix. Use `/` to\nmatch every request. If the request doesn't match one of these prefixes\nthe filter is ignored for purposes of determining if the request is\nblocked or allowed.\n\n* **rule**: Should the filter `block` (blacklist) or `allow` (whitelist)\nthe addresses. This directive is mandatory. It is an error to use it more\nthan once per ipfilter block. The **rule** in effect for the last `ipfilter`\nblock to match a request determines if it is blocked or allowed.\n\n  Note that if you only have `ipfilter` blocks that specify `rule allow`\n  then any request which doesn't match those filters will be implicitly\n  blocked.\n\n* **ip**: A sequence of IP adddresses or CIDR ranges to match. For example,\n`ip 1.2.3.4 192.168.0.0/24` This is optional. It can be used more than\nonce in each `ipfilter` block rather than enumerating all IPs after a single\n`ip` directive.\n\n* **prefix_dir**: Specifies a directory in which to search for file names\nmatching the IP address of the request. This is optional. It is an error\nto use this more than once per `ipfilter` block.\n\n  You can specify a relative pathname to place it relative to the Caddy\n  server CWD (which should be the content root dir).  When putting the\n  blacklisted directory in the web server document tree you should also add\n  an `internal` directive to ensure those files are not visible via HTTP\n  GET requests. For example, `internal /blacklist/`. You can also specify\n  an absolute pathname to locate the blacklist directory outside the\n  document tree. And the path can include environment vars. For example,\n  `prefix_dir {$HOME}/etc/www/blacklist`.\n\n  You can create the file in the root of the blacklist directory. This is\n  known as using a \"flat\" namespace. For example, *blacklist/127.0.0.1*\n  or *blacklist/2601:647:4601:fa93:1865:4b6c:d055:3f3*. However,\n  putting thousands of files in a single directory may cause\n  poor performance of the lookup function. So you can also,\n  and should, use a \"sharded\" namespace. This involves creating\n  the file in a subdirectory based on the first two components\n  of the address. For example, *blacklist/127/0/127.0.0.1* or\n  *blacklist/2601/647/2601:647:4601:fa93:1865:4b6c:d055:3f3*.\n\n  **Note:** IPv6 addresses as file names can use\n  colons or equal-signs to separate the components; e.g.,\n  *blacklist/2601/647/2601=647=4601=fa93==3f3*. Using equal-signs in\n  place of colons in the file name may be necessary on platforms like MS\n  Windows which assign special meaning to colons in file names. You have\n  to use one or the other; you cannot mix them in the same file name.\n\n  Note that you can also whitelist IP addresses using this mechanism\n  by specifying `rule allow`. This may be useful when it follows a more\n  general blocking rule (e.g., by country) and you want to selectively\n  allow some addresses through but don't want to hardcode the addresses\n  in the Caddy config file.\n\n  This mechanism is most useful when coupled with automated monitoring of\n  your web server activity to detect signals that your server is under\n  attack from malware. All your monitoring software has to do is create\n  a file in the blacklist directory.\n\n  At this time the content of the file is ignored. In the future the\n  contents will probably be read and exposed as a placeholder variable\n  for use in conjuction with a template to be filled in via the `markdown`\n  directive. So you should consider putting some explanatory text in the\n  file explaining why the address was blocked.\n\n* **database**: Specifies the path to a\n[MaxMind](https://dev.maxmind.com/geoip/geoip2/geolite2/) database. This\nis required if using the **country** directive; otherwise it should\nbe omitted.\n\n* **country**: A whitespace separated sequence of ISO two letter country\ncodes to filter. This is optional but if used also requires a **database**\ndirective. Note that if a country could not be found for the address it\nwill be the empty string. This can be specified more than once per block\nrather than enumerating all countries on a single line.\n\n* **blockpage**: Names the file to be returned if the ipfilter\nmatches. Note that a `http.StatusOK` (200) status is returned if the\npage is successfully returned to the client. This is optional. If not\nspecified then a `http.StatusForbidden` (403) status is returned.\n\n* **strict**: Use this to disallow use of the address in the\n`X-Forwarded-For` request header if any. This is optional and defaults\nto false. If true or there is no `X-Forwarded-For` header use the address\nfrom the request remote address.\n\n## Caddyfile examples\n\n#### Filter clients based on a given IP or range of IPs\n\n```\nipfilter / {\n\trule block\n\tip 70.1.128.0/19 2001:db8::/122 9.12.20.16\n}\n```\n`caddy` will block any clients with IPs that fall into one of these two ranges `70.1.128.0/19` and `2001:db8::/122` , or a client that has an IP of `9.12.20.16` explicitly.\n\n```\nipfilter / {\n\trule allow\n\tblockpage default.html\n\tip 55.3.4.20 2e80::20:f8ff:fe31:77cf\n}\n```\n`caddy` will serve only these 2 IPs, eveyone else will get `default.html`\n\n```\nipfilter / {\n\trule block\n\tprefix_dir blacklisted\n}\n```\n`caddy` will block any client IP that appears as a file name in the\n*blacklisted* directory.\n\n#### Filter clients based on their [Country ISO Code](https://en.wikipedia.org/wiki/ISO_3166-1#Current_codes)\n\nfiltering with country codes requires a local copy of the Geo database, can be downloaded for free from [MaxMind](https://dev.maxmind.com/geoip/geoip2/geolite2/)\n```\nipfilter / {\n\trule allow\n\tdatabase /data/GeoLite.mmdb\n\tcountry US JP\n}\n```\nwith that in your `Caddyfile` caddy will only serve users from the `United States` or `Japan`\n\n```\nipfilter /notglobal /secret {\n\trule block\n\tdatabase /data/GeoLite.mmdb\n\tblockpage default.html\n\tcountry US JP\n}\n```\nhaving that in your `Caddyfile` caddy will ignore any requests from `United States` or `Japan` to `/notglobal` or `/secret` and it will show `default.html` instead, `blockpage` is optional.\n\n#### Using mutiple `ipfilter` blocks\n\nThe `ipfilter` blocks are evaluated for each HTTP request in the order they\nappear. The last rule which matches a request is used to decide if the request\nis allowed. So in general you will want more general rules (e.g., blacklist an\nentire country) to appear before more specific rules (e.g., to whitelist\nspecific address ranges).\n\n```\nipfilter / {\n\trule allow\n\tip 32.55.3.10\n}\n\nipfilter /webhook {\n\trule allow\n\tip 192.168.1.0/24\n}\n```\nYou can use as many `ipfilter` blocks as you please, the above says: block everyone but `32.55.3.10`, Unless it falls in `192.168.1.0/24` and requesting a path in `/webhook`. Note that this is slightly subtle. Any request doesn't match any of those filters is implicitly blocked. In other words, there is no need to explicitly block every  address followed by \"allow\" filters like those above.\n\n## Backward compatibility\n\n`ipfilter` supports [CIDR notation](https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing). This is the recommended way of specifiying ranges. The old formats of ranging over IPs will get converted to CIDR via [range2CIDRs](https://github.com/pyed/ipfilter/blob/master/range2CIDRs.go) for the purpose of backward compatibility.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpyed%2Fipfilter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpyed%2Fipfilter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpyed%2Fipfilter/lists"}