
🚀 Automatic WordPress login in PHP: snippet for demo access
Demo access to the WordPress admin panel is a standard approach for selling plugins and themes. A potential buyer visits the site, sees the login and password, copies them, goes to wp-login.php, pastes... Too many steps. Each extra click filters out part of the audience.
Automatic login via link solves this problem radically: one link, and the user is already in the admin panel, under the required account. No copy-paste, no confusion with credentials.
Below is a ready-made PHP snippet that adds this mechanism to any WordPress site. Twenty lines of code, customization takes 2 minutes. The snippet doesn't depend on the theme, doesn't pull in external dependencies, and activates like a regular plugin.
💡 Quick overview:
- You add a URL parameter to the login address (for example
?autologin=demo), andwp_signon()authorizes the user under the specified account with a redirect to the required section - The snippet is formatted as a separate WordPress plugin: activate it and it works immediately, deactivate it and it's disabled, the theme is not tied to it
- The basic version serves one account, the extended version supports any number of accounts with different roles and destination points
- Customization for your project: change three values in the code (login, password, URL key), upload the plugin to the site and get login via link
How auto-login works: wp_signon mechanics
The core of the entire structure is the wp_signon() function. It accepts credentials: login, password and remember flag, then authorizes the user exactly like the standard form on wp-login.php. The difference is that the call happens programmatically, without human participation. The user simply follows the link and ends up inside.
The after_setup_theme hook fires before headers are sent, which is critical because wp_signon() sets an auth cookie, and cookies must be sent before any output to the browser. If you hang the call on init or a later hook, the login may not work. That's precisely why the snippet is formatted as a separate plugin rather than inserted into the theme's functions.php: the plugin loads at the very early stages of the WordPress lifecycle, guaranteeing that the hook will execute on time.

Here's the minimal working plugin:
1 <?php 2 /* 3 Plugin Name: Auto Login 4 Plugin URI: https://techblog.sdstudio.top/ 5 Version: 1.0.0 6 Author: Harri Bell-Thomas 7 */ 8 9 function autologin() { 10 if ( $_GET['autologin'] !== 'demo' ) { 11 return; 12 } 13 14 $creds = array( 15 'user_login' => 'demo', 16 'user_password' => 'demo', 17 'remember' => true, 18 ); 19 20 $user = wp_signon( $creds, false ); 21 22 if ( ! is_wp_error( $user ) ) { 23 wp_redirect( admin_url() ); 24 exit; 25 } 26 } 27 add_action( 'after_setup_theme', 'autologin' );
Let's break it down line by line. The condition $_GET['autologin'] === 'demo' checks that the required parameter is passed in the URL: wp-login.php?autologin=demo. The $creds array contains the login, password and "remember" flag. wp_signon() with the false parameter means: don't use secure cookie (suitable for local development and staging sites without HTTPS). On success, wp_redirect( admin_url() ) redirects the user to the admin panel. On error (incorrect password, non-existent user), WordPress shows the standard login form without revealing that there was an auto-login attempt.
Customization for your project
To adapt the snippet, it's sufficient to change three values: the URL parameter, login and password. In the next block they're moved to the beginning of the function for convenience:
1 function autologin() { 2 $param = 'autologin'; 3 $user = 'dummy'; 4 $pass = 'pa55word'; 5 6 if ( $_GET[ $param ] !== $user ) { 7 return; 8 } 9 10 $creds = array( 11 'user_login' => $user, 12 'user_password' => $pass, 13 'remember' => true, 14 ); 15 16 $login = wp_signon( $creds, false ); 17 18 if ( ! is_wp_error( $login ) ) { 19 wp_redirect( admin_url() ); 20 exit; 21 } 22 }
Three rules when customizing that will save you hours of debugging.
First: the value of the $user parameter must not match WordPress reserved keys. Four keys are reserved: loggedout for logging out, action for password reset, redirect_to for redirects after login, and wp_lang for switching locale. If you take any of these words, auto-login will either not work or break the standard core behavior.
Second: the password is stored as plain text in the plugin code, so never use this snippet for administrator or editor accounts on a live site. A demo user with the Subscriber role or, at most, Editor, is acceptable. An administrator with auto-login via link is a hole that an attacker will find in a second.
Third: if the site runs on HTTPS, replace false with true in the second argument of wp_signon(). This will enable secure cookie and prevent auth token interception during network transmission.
The login address will look like this: https://your-site.com/wp-login.php?autologin=dummy. When following it, the user instantly ends up in the admin panel. No intermediate screens, no copy-paste of credentials.
Multi-account: one link, different accounts
What if the demo site shows the product from different roles? The administrator sees the settings panel, the editor sees the publishing interface, the subscriber sees the personal account. For this scenario, we extend the snippet to support multiple accounts:
1 <?php 2 /* 3 Plugin Name: Auto Login 4 Plugin URI: https://techblog.sdstudio.top/ 5 Description: Automatic login for demo accounts. Configure accounts in the array below. 6 Version: 1.0.0 7 Author: Harri Bell-Thomas 8 */ 9 10 $autologin_param = 'autologin'; 11 12 $autologin_accounts = array( 13 array( 14 'user' => 'demo', 15 'pass' => 'demo', 16 'location' => 'wp-admin', 17 ), 18 array( 19 'user' => 'editor_demo', 20 'pass' => 'demo', 21 'location' => 'wp-admin/post-new.php', 22 ), 23 ); 24 25 function autologin() { 26 global $autologin_param, $autologin_accounts; 27 28 foreach ( $autologin_accounts as $account ) { 29 if ( $_GET[ $autologin_param ] === $account['user'] ) { 30 $creds = array( 31 'user_login' => $account['user'], 32 'user_password' => $account['pass'], 33 'remember' => true, 34 ); 35 36 $user = wp_signon( $creds, false ); 37 38 if ( ! is_wp_error( $user ) ) { 39 wp_redirect( admin_url( $account['location'] ) ); 40 exit; 41 } 42 } 43 } 44 } 45 add_action( 'after_setup_theme', 'autologin' );
The mechanics are the same, but the $autologin_accounts array stores the credentials of all demo users, and foreach iterates through them looking for a match by URL parameter value. Adding a new account comes down to copying the array block with new user, pass and location values.
The location parameter accepts any relative path inside /wp-admin/: for example, options-general.php for the settings page, edit.php for the posts list, or post-new.php for creating a new post. You can also specify a full URL for redirecting to the frontend, but then replace admin_url( $account['location'] ) with $account['location'] in the code.
The entry point is the same: https://your-site.com/wp-login.php?autologin=editor_demo, and the user lands on the post creation page rather than the admin root. For the demo site owner, this means full control over the first impression: each type of user sees exactly that part of the product that's relevant to their interests.
⁉️🤔 Frequently asked questions
Is it safe to store passwords in plugin code?
No, it's not safe. The snippet was designed for demo sites where accounts are intentionally public and don't have real privileges. On a production site with real users, this approach is unacceptable: the password in plain text can leak during any code audit. For production scenarios, consider a one-time token mechanism or SSO via REST API with nonce verification.
Why not the wp_login action, but directly wp_signon?
The
wp_loginhook fires after a successful login, it doesn't perform the authorization itself.wp_signon()is the function that authenticates the user: it checks the password viawp_authenticate(), sets the cookie, and triggerswp_loginas a consequence. If you need to programmatically authorize a user only,wp_signonis your tool.
Can I insert the snippet into functions.php instead of a separate plugin?
Yes, the code will work in the theme's
functions.phptoo. But a plugin is more convenient: auto-login isn't tied to the theme and won't break when switching themes. Plus, the plugin can be deactivated with one click without editing files on the server.
What to do if after login the user is not authorized in is_user_logged_in?
This is a known peculiarity of
wp_signon()when called before theinithook: the cookie is set, but the global$current_userisn't updated yet. The solution is to forcibly callwp_set_current_user( $user->ID )immediately after a successfulwp_signon(). In our snippet this isn't critical because a redirect follows immediately, but in scenarios without redirect the line is mandatory.
Does the snippet work on multisite?
Yes, but with a caveat.
wp_signon()authorizes the user within the current site in the network. If the account exists only on one of the subsites, auto-login won't work on others. For cross-authentication through the entire network, use a combination ofwp_signon()andwp_set_auth_cookie()with forced cookie installation for the main domain.
Auto-login snippet: what's the bottom line
Fifteen lines of PHP eliminate the main friction in the demo site funnel: copying login and password. One link, and the visitor is inside the product, with the role and in the section you chose.
The basic version covers the "one demo user" scenario, the extended one covers any number of roles with different redirect points. Both don't depend on the theme, don't require third-party plugins, and activate in a minute.
The main security rule is don't elevate privileges. A demo user with the Subscriber role or, at most, Editor is acceptable. An administrator with auto-login via link is a hole that an attacker will find in a second.
If demo access is a sales channel for you, automatic login stops being a "nice addition" and becomes a mandatory conversion element. One link instead of login and password is the difference between "I'll try later" and "I'll try now".



