PHPackages                             johnfmorton/login-lockdown - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Security](/categories/security)
4. /
5. johnfmorton/login-lockdown

ActiveCraft-plugin[Security](/categories/security)

johnfmorton/login-lockdown
==========================

Prevents brute force password attacks on the Craft CMS control panel by tracking failed login attempts per IP address and blocking access after a configurable threshold.

1.0.3(1mo ago)049↓100%[1 PRs](https://github.com/johnfmorton/craft-login-lockdown/pulls)MITPHPPHP &gt;=8.2CI passing

Since Feb 2Pushed 1w agoCompare

[ Source](https://github.com/johnfmorton/craft-login-lockdown)[ Packagist](https://packagist.org/packages/johnfmorton/login-lockdown)[ RSS](/packages/johnfmorton-login-lockdown/feed)WikiDiscussions main Synced today

READMEChangelog (1)Dependencies (2)Versions (8)Used By (0)

Login Lockdown Documentation
============================

[](#login-lockdown-documentation)

Login Lockdown is a Craft CMS 5 plugin that protects your login forms from brute force password attacks by tracking failed login attempts per IP address and temporarily blocking access after a configurable threshold is reached.

How It Works
------------

[](#how-it-works)

1. When a user fails to log in (to either the Control Panel or a front-end login form), the plugin records the attempt along with their IP address
2. If the same IP address exceeds the maximum allowed failed attempts within the configured time window, the IP is blocked
3. Blocked IPs receive a 403 Forbidden response when attempting to log in via the Control Panel or front-end forms
4. After the lockout duration expires, the IP is automatically unblocked
5. Whitelisted IPs are never blocked, regardless of failed attempts

**Note:** Both Control Panel and front-end login forms are protected by default. Front-end protection can be disabled in the settings if desired.

The plugin is proxy-aware and correctly identifies client IPs behind Cloudflare, load balancers, and reverse proxies by checking headers in this order:

- `CF-Connecting-IP` (Cloudflare)
- `X-Forwarded-For` (standard proxy header)
- `X-Real-IP` (nginx proxy header)
- `REMOTE_ADDR` (direct connection)

Installation
------------

[](#installation)

Install via Composer:

```
composer require johnfmorton/login-lockdown
```

Then install the plugin in Craft:

```
php craft plugin/install login-lockdown
```

Or install via the Control Panel under **Settings → Plugins**.

Configuration
-------------

[](#configuration)

Configure the plugin via **Settings → Login Lockdown** in the control panel.

**All settings support environment variables** using the `$ENV_VAR` syntax. For example, set a field to `$MY_VAR` and define the value in your `.env` file.

### Protection Settings

[](#protection-settings)

SettingDefaultTypeDescriptionEnable Protection`true`booleanEnable or disable brute force protectionProtect Front-End Login Forms`true`booleanBlock login attempts from blocked IPs on front-end forms (not just CP)Maximum Failed Attempts`5`integerNumber of failed login attempts before blockingAttempt Window`900`integerTime window in seconds for counting failed attempts (15 min)Lockout Duration`86400`integerHow long to block an IP in seconds (24 hours)Block Message(see below)stringMessage displayed to blocked usersWhitelisted IP Addresses(none)arrayIPs that should never be blocked**Default block message**: "Access temporarily blocked due to too many failed login attempts. Please try again later."

### Notification Settings

[](#notification-settings)

SettingDefaultTypeDescriptionEnable Notifications`false`booleanSend notifications when an IP is blockedNotification Email(empty)stringEmail address for block notificationsEnable Pushover`false`booleanSend push notifications via PushoverPushover User Key(empty)stringYour Pushover user keyPushover API Token(empty)stringYour Pushover application API token### Environment Variable Support

[](#environment-variable-support)

All settings support Craft CMS environment variable syntax. In the control panel, enter a value like `$MY_ENV_VAR` and define the actual value in your `.env` file.

#### Boolean Values

[](#boolean-values)

For boolean settings (Enable Protection, Enable Notifications, Enable Pushover), use one of:

- `true`, `1`, `yes`, `on` for enabled
- `false`, `0`, `no`, `off` for disabled
- Or an environment variable like `$LOGIN_LOCKDOWN_ENABLED`

#### Example Setup

[](#example-setup)

1. In your `.env` file:

```
# Protection settings
LOGIN_LOCKDOWN_ENABLED=true
LOGIN_LOCKDOWN_PROTECT_FRONTEND=true
LOGIN_LOCKDOWN_MAX_ATTEMPTS=3
LOGIN_LOCKDOWN_ATTEMPT_WINDOW=600
LOGIN_LOCKDOWN_LOCKOUT_DURATION=3600

# Notification settings
LOGIN_LOCKDOWN_NOTIFY=true
SECURITY_EMAIL=security@example.com

# Pushover credentials
PUSHOVER_ENABLED=true
PUSHOVER_USER_KEY=your_pushover_user_key_here
PUSHOVER_API_TOKEN=your_pushover_api_token_here
```

2. In the plugin settings (Control Panel → Settings → Login Lockdown):
    - Set **Enable Protection** to `$LOGIN_LOCKDOWN_ENABLED`
    - Set **Protect Front-End Login Forms** to `$LOGIN_LOCKDOWN_PROTECT_FRONTEND`
    - Set **Maximum Failed Attempts** to `$LOGIN_LOCKDOWN_MAX_ATTEMPTS`
    - Set **Attempt Window** to `$LOGIN_LOCKDOWN_ATTEMPT_WINDOW`
    - Set **Lockout Duration** to `$LOGIN_LOCKDOWN_LOCKOUT_DURATION`
    - Set **Enable Notifications** to `$LOGIN_LOCKDOWN_NOTIFY`
    - Set **Notification Email** to `$SECURITY_EMAIL`
    - Set **Enable Pushover** to `$PUSHOVER_ENABLED`
    - Set **Pushover User Key** to `$PUSHOVER_USER_KEY`
    - Set **Pushover API Token** to `$PUSHOVER_API_TOKEN`

This approach keeps sensitive credentials out of your project config and allows different values per environment.

### Common Time Values

[](#common-time-values)

DurationSeconds5 minutes`300`15 minutes`900`30 minutes`1800`1 hour`3600`6 hours`21600`12 hours`43200`24 hours`86400`7 days`604800`Managing Blocked IPs
--------------------

[](#managing-blocked-ips)

The plugin adds a "Login Lockdown" section to the control panel navigation where you can:

- View all currently blocked IP addresses
- See when each IP was blocked and when the block expires
- See the number of failed attempts that triggered the block
- Manually unblock IP addresses
- Manually block IP addresses

Notifications
-------------

[](#notifications)

When enabled, the plugin can send notifications each time an IP is blocked:

### Email Notifications

[](#email-notifications)

Requires:

- `Enable Notifications` set to `true` (or env var resolving to true)
- Valid email address in `Notification Email` (or environment variable reference)
- Craft's email settings configured correctly

### Pushover Notifications

[](#pushover-notifications)

[Pushover](https://pushover.net/) is a service for sending push notifications to mobile devices.

Requires:

- `Enable Notifications` set to `true`
- `Enable Pushover` set to `true`
- Valid `Pushover User Key` (from your Pushover account)
- Valid `Pushover API Token` (create an application in Pushover)

Whitelisting IPs
----------------

[](#whitelisting-ips)

Whitelisted IPs are never blocked, even if they exceed the failed attempt threshold. Use this for:

- Your office IP address
- Trusted administrator IPs
- Automated testing systems

Add whitelisted IPs via the Control Panel settings page using the editable table.

Database Tables
---------------

[](#database-tables)

The plugin creates two database tables:

### `loginlockdown_login_attempts`

[](#loginlockdown_login_attempts)

Stores failed login attempts:

- `ipAddress`: The IP that made the attempt
- `username`: The username that was tried
- `userAgent`: The browser/client user agent
- `dateAttempted`: When the attempt occurred

### `loginlockdown_blocked_ips`

[](#loginlockdown_blocked_ips)

Stores blocked IPs:

- `ipAddress`: The blocked IP
- `attemptCount`: Number of failed attempts
- `reason`: Why the IP was blocked
- `blockedUntil`: When the block expires
- `isManual`: Whether this was a manual block

CLI Commands
------------

[](#cli-commands)

The plugin provides Craft CLI commands for managing blocked IPs and cleaning up old records.

### Cleanup Command

[](#cleanup-command)

Clean up old login attempt records and expired blocks:

```
# Delete records older than 30 days (default)
php craft login-lockdown/cleanup

# Delete records older than 7 days
php craft login-lockdown/cleanup --days=7
php craft login-lockdown/cleanup -d 7
```

### Block Management Commands

[](#block-management-commands)

List, add, remove, and check blocked IPs:

```
# List currently blocked IPs
php craft login-lockdown/block/list

# Include expired blocks in the list
php craft login-lockdown/block/list --all
php craft login-lockdown/block/list -a

# Block an IP address manually
php craft login-lockdown/block/add 192.168.1.100

# Unblock an IP address
php craft login-lockdown/block/remove 192.168.1.100

# Check if an IP is blocked
php craft login-lockdown/block/check 192.168.1.100
```

Cleanup (Programmatic)
----------------------

[](#cleanup-programmatic)

Old login attempt records and expired blocks are not cleaned up automatically. You can trigger cleanup via the Control Panel, CLI commands (see above), or programmatically via the protection service:

```
use johnfmorton\loginlockdown\LoginLockdown;

// Delete records older than 30 days (default)
LoginLockdown::$plugin->protectionService->cleanup();

// Delete records older than 7 days
LoginLockdown::$plugin->protectionService->cleanup(7);
```

### Scheduled Cleanup

[](#scheduled-cleanup)

To run cleanup automatically, add a cron job to execute the CLI command:

```
# Run daily at 3am (delete records older than 30 days)
0 3 * * * cd /path/to/project && php craft login-lockdown/cleanup

# Run weekly, keeping only 7 days of records
0 3 * * 0 cd /path/to/project && php craft login-lockdown/cleanup --days=7
```

Programmatic Usage
------------------

[](#programmatic-usage)

### Check if an IP is blocked

[](#check-if-an-ip-is-blocked)

```
use johnfmorton\loginlockdown\LoginLockdown;

$ip = '192.168.1.100';
$isBlocked = LoginLockdown::$plugin->protectionService->isBlocked($ip);
```

### Manually block an IP

[](#manually-block-an-ip)

```
use johnfmorton\loginlockdown\LoginLockdown;

LoginLockdown::$plugin->protectionService->blockIp(
    '192.168.1.100',       // IP address
    0,                     // attempt count (0 for manual)
    'Suspicious activity', // reason
    true                   // isManual
);
```

### Manually unblock an IP

[](#manually-unblock-an-ip)

```
use johnfmorton\loginlockdown\LoginLockdown;

LoginLockdown::$plugin->protectionService->unblockIp('192.168.1.100');
```

### Get all blocked IPs

[](#get-all-blocked-ips)

```
use johnfmorton\loginlockdown\LoginLockdown;

// Get only active blocks
$blockedIps = LoginLockdown::$plugin->protectionService->getBlockedIps();

// Include expired blocks
$allBlocks = LoginLockdown::$plugin->protectionService->getBlockedIps(true);
```

Troubleshooting
---------------

[](#troubleshooting)

### I'm locked out of my own site

[](#im-locked-out-of-my-own-site)

If you've accidentally blocked yourself:

1. **Via CLI**: `php craft login-lockdown/block/remove YOUR_IP_ADDRESS`
2. **Via database**: Delete your IP from the `loginlockdown_blocked_ips` table
3. **Disable plugin**: Rename the plugin folder temporarily to disable it

### Blocks aren't working behind a proxy

[](#blocks-arent-working-behind-a-proxy)

Ensure your proxy is sending the correct headers:

- For Cloudflare: The `CF-Connecting-IP` header should be present
- For nginx: Configure `proxy_set_header X-Real-IP $remote_addr;`
- For load balancers: Ensure `X-Forwarded-For` is being passed

### Notifications aren't sending

[](#notifications-arent-sending)

1. **Email**: Verify Craft's email settings are configured and working
2. **Pushover**: Verify your user key and API token are correct (check for typos in env var names)
3. Check the Craft logs for error messages

### Environment variables not working

[](#environment-variables-not-working)

1. Ensure the `.env` file is in the project root
2. Check that the variable name in settings matches exactly (case-sensitive)
3. Verify the `.env` file is being loaded (check other Craft env vars work)
4. Clear Craft's caches after changing `.env` values

Security Recommendations
------------------------

[](#security-recommendations)

1. **Set reasonable thresholds**: 3-5 attempts is usually sufficient
2. **Use shorter attempt windows**: 5-15 minutes catches rapid attacks
3. **Enable notifications**: Be aware of attacks as they happen
4. **Whitelist carefully**: Only whitelist static, trusted IPs
5. **Monitor logs**: Review blocked IPs periodically for patterns
6. **Combine with other security**: Use strong passwords, 2FA, and keep Craft updated
7. **Use environment variables**: Keep sensitive credentials like Pushover keys out of project config
8. **Schedule regular cleanup**: Set up a cron job to periodically clean up old login attempts and expired blocks to keep database tables lean (see [Scheduled Cleanup](#scheduled-cleanup))

License
-------

[](#license)

MIT License - see LICENSE file for details.

Support
-------

[](#support)

- **Issues**: [GitHub Issues](https://github.com/johnfmorton/craft-login-lockdown/issues)
- **Author**: John F Morton ([supergeekery.com](https://supergeekery.com))

###  Health Score

43

—

FairBetter than 91% of packages

Maintenance95

Actively maintained with recent releases

Popularity11

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity51

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~1 days

Total

4

Last Release

47d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/0a358df4a8be015f25754a678f02a7cb4828e3deefddcc4f13e4e6c783a31946?d=identicon)[johnfmorton](/maintainers/johnfmorton)

---

Top Contributors

[![johnfmorton](https://avatars.githubusercontent.com/u/119723?v=4)](https://github.com/johnfmorton "johnfmorton (13 commits)")

---

Tags

craft-plugincraftcmsphpsecuritycmsCraftcraftcmscraft-pluginbrute forcelogin protection

### Embed Badge

![Health badge](/badges/johnfmorton-login-lockdown/health.svg)

```
[![Health](https://phpackages.com/badges/johnfmorton-login-lockdown/health.svg)](https://phpackages.com/packages/johnfmorton-login-lockdown)
```

###  Alternatives

[craftpulse/craft-password-policy

Password Policy plugin

2824.3k1](/packages/craftpulse-craft-password-policy)[born05/craft-csp

Content Security Policy (or CSP) generator using nonces.

1110.0k](/packages/born05-craft-csp)[supercool/buttonbox

A collection of utility field types.

8953.2k1](/packages/supercool-buttonbox)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
