Flask-IP2Proxy
A small Flask extension that checks whether a visitor's IP address is a known proxy / VPN / Tor / data-center address using the IP2Proxy Python library, and stores the result in the Flask session — so any other view or template in the app can reuse it without repeating the lookup.
Installation
From PyPI, once published:
pip install flask-ip2proxy
From this source tree (editable install, for development):
pip install -e ".[dev]"
IP2Proxy and Flask install automatically either way.
How it works
- On the first request in a session, a
before_requesthook checks the visitor's IP against your local IP2Proxy.BINdatabase and stores the result undersession["ip2proxy"]. - On later requests, if the visitor's IP hasn't changed, the cached value is reused — no repeat database queries.
- Any view or Jinja template can read
session["ip2proxy"]directly, or import thecurrent_proxy_infoproxy for convenience.
Quick start
-
Get a database file. This extension requires a IP2Location database BIN file to work. You may obtain a free LITE database or purchase a commercial database as below:
- Free, self-updating "LITE" editions: https://www.ip2location.com/database/lite
- Full commercial editions with ISP/domain/etc: https://www.ip2location.com/database/ip2proxy
Save the
.BINfile somewhere your app can read it. -
Wire it up
from flask import Flask from flask_ip2proxy import IP2ProxyFlask, current_proxy_info app = Flask(__name__) app.config["SECRET_KEY"] = "change-me" app.config["IP2PROXY_DB_PATH"] = "/path/to/IP2PROXY-LITE-PX1.BIN" ip2proxy = IP2ProxyFlask(app) @app.route("/") def index(): if current_proxy_info and current_proxy_info["is_proxy_flagged"]: return "You appear to be using a proxy or VPN." return "Hello!"
Or with the application-factory pattern:
ip2proxy = IP2ProxyFlask() def create_app(): app = Flask(__name__) app.config["IP2PROXY_DB_PATH"] = "..." ip2proxy.init_app(app) return app
-
Try the full demo:
pip install -e . # edit examples/example_app.py to point at your .BIN file python examples/example_app.py
Or the quickstart-style version:
python examples/hello_app.py # then visit http://127.0.0.1:5000/hello/ or /hello/YourName
What's in the result
session["ip2proxy"] is a plain dict (or None if the lookup failed, was
skipped, or the IP couldn't be resolved). Two fields are always present
when a lookup succeeds:
| Key | Meaning |
|---|---|
is_proxy |
Raw code from the library: -1 unknown/error (never stored — see below), 0 not a proxy, 1 a proxy, 2 a data-center/search-engine proxy. |
is_proxy_flagged |
Convenience boolean: True if is_proxy is 1 or 2. This is the field most apps actually want. |
Everything else (proxy_type, country_short, country_long, region,
city, isp, domain, usage_type, asn, as_name, last_seen,
threat, provider, fraud_score, plus ip) is included automatically
whenever your .BIN edition supports it — see "Automatic column
detection" below.
A result with is_proxy == -1 (IP2Proxy's way of saying "couldn't
determine this") is treated as a failed lookup and stored as None
rather than a dict full of placeholder text, matching how a private/local
IP or any other lookup failure is represented.
Reading the data elsewhere
from flask import session
session["ip2proxy"] # plain dict, or None
from flask_ip2proxy import current_proxy_info
current_proxy_info["is_proxy_flagged"] # LocalProxy, behaves like the dict
{{ session.ip2proxy.is_proxy_flagged }}
That last one needs no setup — Flask injects session into every
template automatically, so session.ip2proxy.whatever just works. See
the flask-ip2location README's "Reading the data elsewhere" section for
a fuller walkthrough of this (including a worked example extending
Flask's own quickstart hello.html, mirrored here in examples/).
Automatic column detection
You don't need to know which .BIN edition you're using — the extension
detects available columns from the query result itself, the same idea as
Flask-IP2Location but adapted to how IP2Proxy actually reports it: rather
than only setting attributes for supported fields, IP2Proxy's get_all()
always returns every possible key, filling in the literal text
"NOT SUPPORTED" for anything your database edition doesn't cover. The
extension drops exactly those placeholder entries, so:
- A
PX2LITE database gives youis_proxy,is_proxy_flagged,proxy_type,country_short,country_long,ip— nothing else, because that's allPX2contains. - A higher
PXtier or commercial edition additionally gives youisp,domain,asn,fraud_score, etc., automatically — nothing to configure, and nothing to update if you later upgrade your database.
If you want to keep only a subset regardless of what's available, set
IP2PROXY_FIELDS to an explicit list and it's applied as a filter on top
of auto-detection.
Configuration reference
| Config key | Default | Description |
|---|---|---|
IP2PROXY_DB_PATH |
required | Path to the .BIN database file. |
IP2PROXY_SESSION_KEY |
"ip2proxy" |
Session dict key the result is stored under. |
IP2PROXY_FIELDS |
None (auto) |
Optional allow-list restricting which auto-detected fields are kept, e.g. ["is_proxy_flagged", "proxy_type"]. |
IP2PROXY_AUTO_LOOKUP |
True |
Automatically look up on every request via before_request. Set False and call ip2proxy.refresh() yourself to trigger it manually instead (e.g. only at login or checkout). |
IP2PROXY_SKIP_PRIVATE_IPS |
True |
Skip the database query entirely for loopback/private/reserved IPs (e.g. 127.0.0.1 during local dev), storing None instead of a meaningless result. |
IP2PROXY_TRUST_PROXY_HEADER |
False |
Trust the client-supplied X-Forwarded-For header for the visitor IP. See the caveat below — it's especially relevant here, since a visitor trying to evade proxy detection has every reason to spoof this exact header. |
Notes and caveats
- Proxies/load balancers. If your app runs behind one,
request.remote_addrwill be the proxy's IP, not the visitor's — every visitor would then wrongly show up with your load balancer's own reputation. The correct fix iswerkzeug.middleware.proxy_fix.ProxyFixconfigured for exactly as many trusted hops as you have, shown commented-out inexamples/example_app.py.IP2PROXY_TRUST_PROXY_HEADERtrusts whateverX-Forwarded-Forvalue shows up instead, which a visitor can forge if nothing upstream is sanitizing it — for a security-relevant signal like proxy detection, get this right rather than reaching for the header-trusting shortcut outside of local testing. - This is a signal, not a verdict.
is_proxy_flaggedreflects what a point-in-time IP-range database says about the address, not a live probe of the connection. Ranges get reassigned, home ISPs sometimes share ranges with hosting providers, and some legitimate users genuinely browse from VPNs. Treat it as one input alongside others (rate limiting, account history, etc.) rather than an automatic block/allow decision. - Session storage. As with Flask-IP2Location, Flask's default session
is a signed-but-not-encrypted cookie. Proxy-detection results are
usually fine to store there, but if you widen
IP2PROXY_FIELDSto includefraud_scoreor similar and have stricter requirements, consider a server-side session backend like Flask-Session. - LITE database fields. Free LITE editions only populate the columns their tier covers; anything else is detected automatically and dropped, as described above.
- IPv6. Whether IPv6 is resolved depends on which
.BINedition you download.
Running the tests
pip install -e ".[dev]"
pytest
Tests monkeypatch IP2Proxy.IP2Proxy with a fake in tests/conftest.py
whose get_all() return shape was verified against the real library's
source (not guessed), so no real .BIN database file is needed to run
the suite.
License
See the LICENSE file.
Metadata
Release files for flask-ip2proxy 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| flask_ip2proxy-1.0.0.tar.gz | 17.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| flask_ip2proxy-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 27.5 kB
Release files / flask_ip2proxy-1.0.0.tar.gz
| Download URL | flask_ip2proxy-1.0.0.tar.gz |
|---|---|
| Size | 17.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
1014adc450589e4012c9fb0d003b6ca36e1fc51f64a27d627cad9cd8711a4ccd
|
|
BLAKE2b-256 checksum How to use checksums |
8128f23f1da80dec0a32c4c0b6e1af772e38e41c20af5a3dd62007c384897791
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.10
|
Release files / flask_ip2proxy-1.0.0-py3-none-any.whl
| Download URL | flask_ip2proxy-1.0.0-py3-none-any.whl |
|---|---|
| Size | 10.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
746ba6085d5534229a2b9fcf8e9bf8d5eec26438fd05bd4bde36fb74634f381c
|
|
BLAKE2b-256 checksum How to use checksums |
99d625d5d48a0b85bd7d1fc1de0fc391c12b1b63f6504337bed9db243a97649b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.10
|