Build your login, register and forgot-password pages with any page builder — and lock the rest of the site behind them.
Built by Surefire Studios
A WordPress plugin that lets you design custom login, register and forgot-password pages with page builders like Elementor, and lock down the rest of your content so only logged-in users can reach it. Includes an optional administrator-approval workflow for new registrations.
- Custom Login Page: Replace wp-login.php with your own designed page
- Custom Register Page: Create beautiful registration forms using page builders
- Custom Forgot Password Page: Design password reset pages that match your brand
- Login Redirects: Redirect users to specific pages after login (global or role-based)
- Logout Redirects: Redirect users to specific pages after logout (global or role-based)
- Seamless Integration: Works with any page builder (Elementor, Beaver Builder, etc.)
- Content Protection: Control which pages are accessible to non-logged-in users
- Flexible Access Control: Select specific pages that should remain public
- Custom Redirect: Choose where to redirect unauthorized users
- Lockdown Messages: Show custom messages to restricted users
- Elementor Widgets: Pre-built widgets for all authentication forms
- Shortcodes: Universal shortcodes that work with any page builder
- Responsive Design: Mobile-friendly forms and layouts
- Customizable Styling: Full control over form appearance
- Automatic lockouts: Temporarily block an IP after repeated failed logins
- Covers every login route: wp-login.php, the plugin's form, XML-RPC and the REST API
- Configurable: Set the attempt limit and lockout duration, or switch it off
- Self-healing: Lockouts expire on their own; a successful login clears the counter
- AJAX Forms: Smooth user experience without page reloads
- Real-time Validation: Instant feedback on form inputs
- Admin Bar Integration: Quick lockdown status visibility
- Security Features: Nonce protection and sanitized inputs
- Upload the plugin files to the
/wp-content/plugins/custom-auth-lockdowndirectory - Activate the plugin through the 'Plugins' menu in WordPress
- Go to Settings > Auth & Lockdown to configure the plugin
-
Create Your Pages:
- Create new pages for Login, Register, and Forgot Password
- Design them using your preferred page builder
-
Add Forms to Pages:
- With Elementor: Use the Custom Auth & Lockdown widgets
- With Shortcodes: Add the appropriate shortcodes to your pages
- Other Page Builders: Use shortcodes in text/HTML elements
-
Configure Plugin Settings:
- Go to Settings > Auth & Lockdown
- Select your custom pages in the "Custom Pages" tab
- Optionally disable wp-login.php access
-
Enable Lockdown:
- Go to the "Site Lockdown" tab
- Check "Enable Site Lockdown"
-
Select Allowed Pages:
- Choose which pages non-logged-in users can access
- Your custom auth pages are automatically allowed
-
Configure Messages:
- Set a custom lockdown message
- Choose redirect behavior
-
Global Redirects:
- Go to the Custom Pages tab
- Select a "Login Redirect Page" or enter a "Login Redirect URL"
- This applies to all users unless overridden by role-based settings
-
Role-Based Redirects:
- In the same tab, configure different redirects for each user role
- Administrators might go to the dashboard, subscribers to a members area
- Custom URLs take priority over page selections
-
Redirect Priority:
- URL parameters (redirect_to) have highest priority
- Role-based custom URLs
- Role-based page selections
- Global custom URL
- Global page selection
- Default behavior (admin dashboard for admins, home for others)
-
Global Logout Redirects:
- In the Custom Pages tab, find the "Logout Redirect" settings
- Select a "Logout Redirect Page" or enter a "Logout Redirect URL"
- This applies to all users unless overridden by role-based settings
-
Role-Based Logout Redirects:
- Configure different logout destinations for each user role
- Premium users might go to a "Thanks for visiting" page
- Administrators might stay on the admin area
- Custom URLs take priority over page selections
-
Logout Priority:
- URL parameters (redirect_to) have highest priority
- Role-based custom URLs
- Role-based page selections
- Global custom URL
- Global page selection
- Default behavior (home page)
[cal_login_form]
[cal_register_form]
[cal_forgot_password_form]
[cal_user_info field="display_name"]
[cal_logout_link text="Sign Out"]
[cal_login_status]
[cal_login_form redirect="https://example.com" show_register_link="true" show_forgot_password_link="true"]
[cal_register_form show_login_link="true"]
[cal_user_info field="display_name" show_avatar="true" avatar_size="50"]
Available fields: display_name, username, email, first_name, last_name, full_name
[cal_logout_link text="Sign Out" redirect="https://example.com"]
When Elementor is active, you'll find these widgets in the "Custom Auth & Lockdown" category:
- Login Form Widget: Complete login form with styling options
- Register Form Widget: User registration form
- Forgot Password Widget: Password reset request form
- User Info Widget: Display logged-in user information
- Logout Link Widget: Customizable logout link
Each widget includes extensive styling options and content controls.
cal_is_page_allowed: Control page access programmatically
add_filter('cal_is_page_allowed', function($is_allowed, $page_id, $post) {
// Custom logic here
return $is_allowed;
}, 10, 3);cal_login_redirect_url: Control login redirect destination
add_filter('cal_login_redirect_url', function($redirect_url, $user, $default_redirect) {
// Redirect based on custom logic
if ($user->has_cap('manage_options')) {
return admin_url('dashboard.php');
}
return home_url('/members-area/');
}, 10, 3);cal_logout_redirect_url: Control logout redirect destination
add_filter('cal_logout_redirect_url', function($redirect_url, $user, $default_redirect) {
// Redirect based on custom logic
if ($user->has_cap('manage_options')) {
return admin_url(); // Keep admins in admin area
}
return home_url('/goodbye/'); // Send others to goodbye page
}, 10, 3);cal_allow_public_rest_request: Allow specific unauthenticated REST requests through the lockdown
add_filter('cal_allow_public_rest_request', function($allow) {
// Keep one public endpoint reachable while the rest of the site is locked down.
if (strpos($_SERVER['REQUEST_URI'], '/wp-json/contact-form/v1/') !== false) {
return true;
}
return $allow;
});cal_after_login: Triggered after successful login
add_action('cal_after_login', function($user) {
// Custom logic after login
});cal_after_register: Triggered after successful registration
add_action('cal_after_register', function($user_id) {
// Custom logic after registration
});The plugin uses semantic CSS classes that you can target in your theme:
.cal-form: All forms.cal-login-form: Login form specifically.cal-register-form: Register form specifically.cal-form-group: Form field groups.cal-submit-btn: Submit buttons.cal-message: Status messages.cal-lockdown-container: Lockdown message container
/* Customize form appearance */
.cal-form {
background: #f8f9fa;
border-radius: 10px;
padding: 30px;
}
.cal-submit-btn {
background: linear-gradient(45deg, #007cba, #005a87);
border-radius: 25px;
}
/* Style lockdown page */
.cal-lockdown-container {
background: url('your-bg-image.jpg') center/cover;
}- All forms use WordPress nonces for CSRF protection
- Input is sanitized and validated; passwords are handled by WordPress core
(
wp_signon,get_password_reset_key,check_password_reset_key,reset_password) - Every admin AJAX action requires both a valid nonce and the
manage_optionscapability - Login redirects are passed through
wp_validate_redirect(), soredirect_tocannot send a visitor off-site - The forgot-password form returns the same response whether or not the account exists, so it cannot be used to enumerate usernames or email addresses
- Repeated failed logins from one IP trigger a temporary lockout (see below)
Enabled by default: after 5 failed attempts an address is blocked for 15 minutes, both
configurable under Settings → Auth & Lockdown. Because it hooks WordPress's own
authenticate filter, it applies to every login route at once — wp-login.php, the plugin's
form, XML-RPC and the REST API — rather than only the form this plugin renders.
Attempts are counted per IP using REMOTE_ADDR. Forwarded-for headers are not trusted by
default: they are trivially spoofed, and honouring them would let an attacker both dodge their
own lockout and lock out someone else. Behind a genuine reverse proxy or CDN, supply the real
address yourself:
add_filter('cal_client_ip', function($ip) {
// Only do this if the proxy is trusted and sets this header itself.
return $_SERVER['HTTP_CF_CONNECTING_IP'] ?? $ip;
});A cal_login_lockout action fires whenever a client is locked out, if you want to log or
alert on it:
add_action('cal_login_lockout', function($ip, $username, $duration) {
error_log("Locked out {$ip} after repeated failures for '{$username}'");
}, 10, 3);When lockdown is enabled, logged-out visitors are blocked from:
- Any page not on the allow-list (the login, register and forgot-password pages are always allowed)
- Posts, archives, search and other non-page content
- RSS/Atom feeds, which would otherwise republish the content you are protecting
- The REST API —
/wp-json/is unauthenticated by default, and without this/wp-json/wp/v2/postswould hand out your content regardless of the lockdown
robots.txt stays reachable, since it contains only crawl directives.
If you need a specific unauthenticated REST request to keep working — a public contact form,
for example — allow it with the cal_allow_public_rest_request filter (see below).
Note
Lockdown restricts front-end delivery. It is not a substitute for file permissions or for keeping private files out of the uploads directory.
Q: Login form doesn't work
A: Make sure you've added the [cal_login_form] shortcode to your custom login page
Q: Users can still access wp-login.php A: Enable "Disable WP Login Access" in plugin settings
Q: Lockdown isn't working A: Check that "Enable Site Lockdown" is checked and you've selected allowed pages
Q: Elementor widgets don't appear A: Make sure Elementor is active and check the "Custom Auth & Lockdown" widget category
Enable WordPress's own debug logging in wp-config.php and check wp-content/debug.log:
define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true);- WordPress 5.0 or higher
- PHP 7.4 or higher
- MySQL 5.6 or higher
- Elementor (for Elementor widgets)
- Any page builder that supports shortcodes
Added
- Brute-force protection. Repeated failed logins from one IP now trigger a temporary lockout (5 attempts / 15 minutes by default, both configurable, and switchable off). It hooks the
authenticatefilter, so it covers wp-login.php, the plugin's form, XML-RPC and the REST API together. Newcal_client_ipfilter andcal_login_lockoutaction.
Cleanup
- Removed a dead
clearCachepath in the admin script: it posted to acal_clear_cacheaction that has no PHP handler, and nothing rendered the element that triggered it. - Escaped role keys used in settings field names.
Security
- Lockdown now covers the REST API.
/wp-json/was exempt, so/wp-json/wp/v2/postsand/pagesserved content to anyone while the site was "locked down". Addcal_allow_public_rest_requestto re-open specific endpoints. - Lockdown now covers feeds.
is_feed()was explicitly allowed, so/feed/republished protected content. - Fixed an open redirect in the login form:
redirect_towas returned verbatim and assigned towindow.location.href. It now passes throughwp_validate_redirect(). - Fixed user enumeration in the forgot-password form, which answered differently for real and unknown accounts.
Fixes
- The admin script was never localized, so
cal_admin/cal_ajaxwere undefined and every admin AJAX request sent an empty nonce. The entire user-approval workflow (approve, reject, bulk actions, migrate, emergency disable) failed. Now localized. register_activation_hook()was called frominit, far too late to ever fire, so activation never created the default options. Now registered at load time.- The nav-menu filter ran
DOMDocument::saveHTML()over the rendered menu, injecting a doctype,<html>and<body>into the middle of it, removed<a>elements while leaving empty<li>s, mutated a live node list mid-iteration, and stripped the login/register links. Replaced with awp_nav_menu_objectsfilter. - The
cal_after_loginandcal_after_registeractions were documented but never fired. They now do. - Guarded missing
$_POSTkeys that raised PHP 8 "Undefined array key" warnings. - Admin CSS/JS now load only on the plugin's settings screen and the Users list.
- Lockdown redirects use
wp_safe_redirect().
- Initial release
- Custom authentication pages
- Site lockdown functionality
- Elementor integration
- Comprehensive shortcode system
For support, feature requests, or bug reports, please create an issue in the plugin repository or contact Surefire Studios.
Issues and pull requests are welcome — see CONTRIBUTING.md.
Caution
Found a security vulnerability? Don't open a public issue — follow SECURITY.md to report it privately.
Released under the GNU General Public License v2.0 or later — see LICENSE.
Note: This plugin provides powerful site restriction capabilities. Always test thoroughly on a staging site before deploying to production, especially when enabling site lockdown features.