bespredel/geo-restrict

Middleware to restrict access to Laravel applications based on IP geolocation.

v1.1.0 2025-06-28 07:41 UTC

This package is auto-updated.

Last update: 2025-06-28 07:42:36 UTC


README

Readme EN Readme RU GitHub license Downloads

Latest Version Latest Version Packagist PHP from Packagist Laravel Version

GeoRestrict is a Laravel middleware that restricts access to your application based on the user's IP geolocation (country, region, ASN, city, ISP, etc).

Features

  • Country, region, ASN, city, ISP filtering by IP
  • Multiple geo-services support (priority by order)
  • Geo response caching (can be disabled)
  • Rate limiting for geo service requests
  • Flexible allow/deny rules, including callbacks and time-based restrictions
  • IP whitelist (local addresses are always allowed)
  • Route targeting via patterns and HTTP methods
  • Localized error messages (multi-language, easy to extend)
  • Different responses per country/rule
  • Logging for blocked and allowed requests

Installation

  1. Install the package:
composer require bespredel/geo-restrict
  1. Publish the configuration:
php artisan vendor:publish --provider="Bespredel\GeoRestrict\GeoRestrictServiceProvider" --tag=geo-restrict-config
  1. (Optional) Publish language files for customization:
php artisan vendor:publish --provider="Bespredel\GeoRestrict\GeoRestrictServiceProvider" --tag=geo-restrict-lang

Example config config/geo-restrict.php

return [
    'services' => [
        // Example provider with options
        [
            'provider' => \Bespredel\GeoRestrict\Providers\Ip2LocationIoProvider::class,
            'options'  => [
                'api_key' => 'your-ip2location-api-key', // required
                'lang'    => 'en', // optional
            ],
        ],

        // Example provider without options
        \Bespredel\GeoRestrict\Providers\IpWhoIsProvider::class,

        // or
        [
            'provider' => \Bespredel\GeoRestrict\Providers\IpWhoIsProvider::class,
            'options'  => [],
        ],

        // Example array provider
        [
            'name' => 'ipapi.co',
            'url'  => 'https://ipapi.co/:ip/json/',
            'map'  => [
                'country' => 'country_code',
                'region'  => 'region_code',
                'city'    => 'city',
                'asn'     => 'asn',
                'isp'     => 'org',
            ],
        ],

        // Add more services; priority is based on order
    ],

    'geo_services' => [
        'cache_ttl'  => 1440,
        'rate_limit' => 30,
    ],

    'access' => [
        'whitelisted_ips' => ['127.0.0.1'],
        'rules' => [
            'allow' => [
                'country'  => ['RU'],
                'region'   => [],
                'city'     => [],
                'asn'      => [],
                'callback' => null, // function($geo) { return ...; }
            ],
            'deny'  => [
                'country'  => [],
                'region'   => [],
                'city'     => [],
                'asn'      => [],
                'callback' => null, // function($geo) { return ...; }
                'time'     => [
                    // ['from' => '22:00', 'to' => '06:00']
                ],
            ],
        ],
    ],

    'logging' => [
        'blocked_requests' => true,
        'allowed_requests' => false,
    ],

    'block_response' => [
        'type'  => 'abort', // 'abort', 'json', 'view'
        'view'  => 'errors.geo_blocked',
        'json'  => [
            'message' => 'Access denied: your region is restricted.',
        ],
    ],
    
    'routes' => [
        'only'    => [], // ['admin/*', 'api/v1/*']
        'except'  => [],
        'methods' => [], // ['GET', 'POST']
    ],
];

Key parameters explained

  • services — list of geo-services used to resolve location by IP. Each provider supports only its documented parameters (see table below).
  • geo_services.cache_ttl — cache lifetime in minutes (0 disables caching).
  • geo_services.rate_limit — max requests per minute per IP to geo services.
  • access.whitelisted_ips — IPs that are always allowed (e.g., localhost).
  • access.rules.allow/deny — allow/deny rules by country, region, ASN, callbacks and time periods.
  • logging — enable logging of blocked or allowed requests.
  • block_response.type — response type: 'abort', 'json', or 'view'.
  • routes.only/except/methods — route and method matching.

Supported Providers and Parameters

Provider Class Required Params Optional Params
IP2Location.io Ip2LocationIoProvider api_key, ip lang
ipwho.is IpWhoIsProvider ip api_key
ip-api.com IpApiComProvider ip lang
ipapi.co IpApiCoProvider ip lang

Only parameters listed in requiredParams and optionalParams for each provider will be used in the request. If a required parameter is missing, an error will be thrown and logged. Optional parameters are included only if set in config.

Provider architecture

All geo providers now inherit from AbstractGeoProvider and only need to define:

  • baseUrl, endpoint (with :param placeholders)
  • requiredParams, optionalParams
  • responseMap (array for mapping API fields to standard keys)
  • isValidResponse(array $data) (checks if API response is valid)
  • getErrorMessage(array $data) (returns error message for invalid response)

Example of a minimal provider:

class ExampleProvider extends AbstractGeoProvider {
    protected ?string $baseUrl = 'https://example.com/';
    protected ?string $endpoint = 'api/:ip';
    protected array $requiredParams = ['ip'];
    protected array $optionalParams = ['lang'];
    protected array $responseMap = [
        'country' => 'country_code',
        'region'  => 'region',
        'city'    => 'city',
        'asn'     => 'asn',
        'isp'     => 'isp',
    ];
    protected function isValidResponse(array $data): bool {
        return isset($data['country_code']);
    }
    protected function getErrorMessage(array $data): string {
        return 'example.com: invalid response';
    }
    public function getName(): string { return 'example.com'; }
}

This makes it easy to add new providers and ensures all logic is unified and DRY.

Usage

  1. Add the middleware to routes:
Route::middleware(['geo.restrict'])->group(function () {
    // ...
});
  1. Or apply directly:
Route::get('/secret', 'SecretController@index')->middleware('geo.restrict');

Customization

  • Add your geo-services to the services array; order defines priority.
  • Use allow/deny rules for flexible filtering.
  • For complex cases, use callback functions in rules.
  • For localization, use language files.

Localization and Language Files

GeoRestrict supports multi-language block messages. To customize or add new translations:

  1. Publish the language files:
php artisan vendor:publish --provider="Bespredel\GeoRestrict\GeoRestrictServiceProvider" --tag=geo-restrict-lang
  1. Edit files in resources/lang/vendor/geo-restrict/ as needed. Add new locales by creating new folders (e.g., it, es).
  • The block message is automatically shown in the user's country language (based on the country code, if a corresponding language file exists), or in the application's default language.
  • To add a new language, create a file like:
resources/lang/it/messages.php

No code changes are required — the language is detected automatically based on the country code (e.g., IT, FR, DE, RU, EN, etc.).

License

MIT