An open API service indexing awesome lists of open source software.

https://github.com/bespredel/geo-restrict

Laravel middleware to restrict access by user geolocation using IP-based rules, whitelisting, and external GeoIP services.
https://github.com/bespredel/geo-restrict

access geo geoip ip laravel location middleware restriction

Last synced: about 2 months ago
JSON representation

Laravel middleware to restrict access by user geolocation using IP-based rules, whitelisting, and external GeoIP services.

Awesome Lists containing this project

README

          

# GeoRestrict - Advanced GeoIP Middleware for Laravel

[![Readme EN](https://img.shields.io/badge/README-EN-blue.svg)](https://github.com/BespredeL/geo-restrict/blob/master/README.md)
[![Readme RU](https://img.shields.io/badge/README-RU-blue.svg)](https://github.com/BespredeL/geo-restrict/blob/master/README_RU.md)
[![GitHub license](https://img.shields.io/badge/license-MIT-458a7b.svg)](https://github.com/BespredeL/geo-restrict/blob/master/LICENSE)
[![Downloads](https://img.shields.io/packagist/dt/bespredel/geo-restrict.svg)](https://packagist.org/packages/bespredel/geo-restrict)

[![Latest Version](https://img.shields.io/github/v/release/bespredel/GeoRestrict?logo=github)](https://github.com/BespredeL/geo-restrict/releases)
[![Latest Version Packagist](https://img.shields.io/packagist/v/bespredel/geo-restrict.svg?logo=packagist&logoColor=white&color=F28D1A)](https://packagist.org/packages/bespredel/geo-restrict)
[![PHP from Packagist](https://img.shields.io/packagist/php-v/bespredel/geo-restrict.svg?logo=php&logoColor=white&color=777BB4)](https://php.net)
[![Laravel Version](https://img.shields.io/badge/laravel-%3E%3D10-FF2D20?logo=laravel)](https://laravel.com)

🚀 Production-ready GeoIP access control for Laravel applications

GeoRestrict is a powerful and flexible Laravel middleware that allows you to control access to your application based on user geolocation.

It supports filtering by country, region, city, ASN, ISP, and provides advanced features like multi-provider fallback, caching, rate limiting, and
flexible rule configuration.

---

## ✨ Features

- 🌍 Geo-based filtering (country, region, city, ASN, ISP)
- 🔀 Multiple GeoIP providers with fallback support
- ⚡ Built-in caching for performance optimization
- 🚦 Rate limiting for external GeoIP services
- 🧠 Flexible allow/deny rules with callbacks
- ⏱ Time-based access restrictions
- 📍 Route-level targeting (patterns + HTTP methods)
- 🛡 IP whitelist support
- 🌐 Multi-language error responses
- 📊 Logging for allowed and blocked requests

---

## 📦 Installation

1. Install the package via Composer::

```bash
composer require bespredel/geo-restrict
```

2. Publish the config file:

```bash
php artisan vendor:publish --provider="Bespredel\GeoRestrict\GeoRestrictServiceProvider" --tag=geo-restrict-config
```

3. (Optional) Publish language files for customization:

```bash
php artisan vendor:publish --provider="Bespredel\GeoRestrict\GeoRestrictServiceProvider" --tag=geo-restrict-lang
```

---

## ⚙️ Configuration

The configuration file allows you to fully customize how GeoRestrict behaves.

**Example:**

```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/',
'headers' => [
'Accept' => 'application/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,
'provider_timeout' => 5,
'provider_retries' => 0,
'provider_retry_delay_ms' => 150,
],

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

'routes' => [
'only' => [], // ['admin/*', 'api/v1/*']
'except' => [],
'methods' => [], // ['GET', 'POST']
],

'excluded_networks' => [
'127.0.0.1',
'::1',
'10.0.0.0/8',
'192.168.0.0/16',
'172.16.0.0/12',
// Add more as needed
],

'logging' => [
'blocked_requests' => true,
'allowed_requests' => false,
'channel' => 'geo-restrict', // Use your custom channel name
],

'observability' => [
'log_cache_hits' => false,
'log_provider_latency' => false,
'log_deny_reasons' => false,
],

'block_response' => [
'type' => 'abort', // 'abort', 'json', 'view'
'view' => 'errors.geo_blocked',
'json' => [
'message' => 'Access denied: your region is restricted.',
],
]
];
```

---

### 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.
- **geo_services.provider_timeout/provider_retries** - provider timeout and retry policy.
- **access.rules.allow/deny** - allow/deny rules by country, region, ASN, callbacks and time periods.
- **logging** - enable logging of blocked or allowed requests.
- **observability** - optional debug diagnostics for cache hits, latency and deny reasons.
- **block_response.type** - response type: 'abort', 'json', or 'view'.
- **routes.only/except/methods** - route and method matching.
- **excluded_networks** - list of IP networks to exclude from geo restriction.

---

#### 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:**

```php
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.

### Compatibility notes

- `GeoAccess::passesRules()` is still available for backward compatibility.
- New code can use `GeoAccess::evaluateRules()` for a structured rule-check result.

---

## 🚀 Usage

1. Add the middleware to routes:

```php
Route::middleware(['geo-restrict'])->group(function () {
// ...
});
```

2. Or apply directly:

```php
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:

```bash
php artisan vendor:publish --provider="Bespredel\GeoRestrict\GeoRestrictServiceProvider" --tag=geo-restrict-lang
```

2. 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.).

---

## ⚡ Cache Management & Tag-based Cache Flush

GeoRestrict uses Laravel's cache for geo-data and rate limiting. If your cache driver supports tags (Redis, Memcached), all geoip cache entries are
tagged with `geoip`.

- To flush all geoip cache entries at once, use the artisan command:

```bash
php artisan geo-restrict:clear-cache
```

You will see:

GeoIP cache flushed.

> **Note:** Tag-based cache flush is only available for Redis and Memcached drivers. For other drivers, cache will not be flushed in bulk.

---

## 📊 Logging: Custom Channel Support

GeoRestrict supports logging to a custom channel. By default, all logs (blocked/allowed requests, provider errors, rate limits) go to the main Laravel
log. To use a separate channel, set the `logging.channel` parameter in your `config/geo-restrict.php`:

```php
'logging' => [
'blocked_requests' => true,
'allowed_requests' => false,
'channel' => 'geo-restrict', // Use your custom channel name
],
```

Add a channel to your `config/logging.php`:

```php
'channels' => [
// ...
'geo-restrict' => [
'driver' => 'single',
'path' => storage_path('logs/geo-restrict.log'),
'level' => 'info',
],
],
```

If `channel` is not set or is `null`, logs will go to the default log channel.

---

## 📄 License

This package is open-source software licensed under the MIT license.

---

## ⭐ Support

If you find this package useful, consider giving it a star ⭐ on GitHub.