Skip to content
🚀 Automatic WordPress login in PHP: snippet for demo access

🚀 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), and wp_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.

Complete code for WordPress auto-login plugin

Here's the minimal working plugin:

1<?php
2/*
3Plugin Name: Auto Login
4Plugin URI: https://techblog.sdstudio.top/
5Version: 1.0.0
6Author: Harri Bell-Thomas
7*/
8
9function 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}
27add_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:

1function 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.

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/*
3Plugin Name: Auto Login
4Plugin URI: https://techblog.sdstudio.top/
5Description: Automatic login for demo accounts. Configure accounts in the array below.
6Version: 1.0.0
7Author: 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
25function 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}
45add_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_login hook 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 via wp_authenticate(), sets the cookie, and triggers wp_login as a consequence. If you need to programmatically authorize a user only, wp_signon is 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.php too. 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 the init hook: the cookie is set, but the global $current_user isn't updated yet. The solution is to forcibly call wp_set_current_user( $user->ID ) immediately after a successful wp_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 of wp_signon() and wp_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".