{"id":399210,"date":"2026-11-06T08:57:32","date_gmt":"2026-11-06T08:57:32","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/dh-user-session-limiter\/"},"modified":"2026-11-06T08:57:23","modified_gmt":"2026-11-06T08:57:23","slug":"dalmatian-dream-user-session-limiter","status":"publish","type":"plugin","link":"https:\/\/wordpress.org\/plugins\/dalmatian-dream-user-session-limiter\/","author":15344623,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"2.3.9","stable_tag":"2.3.9","tested":"7.1.4","requires":"6.2","requires_php":"7.4","requires_plugins":null,"header_name":"Dalmatian Dream User Session Limiter","header_author":"Boris Horvat","header_description":"Maksimalno trajanje prijave i neaktivnost po korisniku, odvojeno za svaku WordPress sesiju.","assets_banners_color":"95a2aa","last_updated":"2026-11-06 08:57:23","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"","header_author_uri":"https:\/\/horvat.info","rating":0,"author_block_rating":0,"active_installs":0,"downloads":204,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"2.3.9":{"tag":"2.3.9","author":"whitedd","date":"2026-11-06 08:57:23","revision":3787518}},"upgrade_notice":{"2.3.9":"<p>Rename and activation compatibility update. Deactivate older plugin copies before installing the renamed folder.<\/p>","2.3.8":"<p>Security and scheduling update. Test integrations on staging. Multisite and nonstandard session backends are unsupported.<\/p>"},"ratings":[],"assets_icons":{"icon-128x128.png":{"filename":"icon-128x128.png","revision":3787517,"resolution":"128x128","location":"assets","locale":"","width":128,"height":128},"icon-256x256.png":{"filename":"icon-256x256.png","revision":3787517,"resolution":"256x256","location":"assets","locale":"","width":256,"height":256}},"assets_banners":{"banner-1544x500.png":{"filename":"banner-1544x500.png","revision":3787517,"resolution":"1544x500","location":"assets","locale":"","width":1544,"height":500},"banner-772x250.png":{"filename":"banner-772x250.png","revision":3787517,"resolution":"772x250","location":"assets","locale":"","width":772,"height":250}},"assets_blueprints":{},"all_blocks":[],"tagged_versions":["2.3.9"],"block_files":[],"assets_screenshots":[],"screenshots":[]},"plugin_section":[],"plugin_tags":[9839,4233,600,30471],"plugin_category":[38,54],"plugin_contributors":[],"plugin_business_model":[],"class_list":["post-399210","plugin","type-plugin","status-publish","hentry","plugin_tags-inactivity","plugin_tags-logout","plugin_tags-security","plugin_tags-sessions","plugin_category-authentication","plugin_category-security-and-spam-protection","plugin_committers-whitedd"],"banners":{"banner":"https:\/\/ps.w.org\/dalmatian-dream-user-session-limiter\/assets\/banner-772x250.png?rev=3787517","banner_2x":"https:\/\/ps.w.org\/dalmatian-dream-user-session-limiter\/assets\/banner-1544x500.png?rev=3787517","banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":false,"icon":"https:\/\/ps.w.org\/dalmatian-dream-user-session-limiter\/assets\/icon-128x128.png?rev=3787517","icon_2x":"https:\/\/ps.w.org\/dalmatian-dream-user-session-limiter\/assets\/icon-256x256.png?rev=3787517","generated":false},"screenshots":[],"raw_content":"<!--section=description-->\n<p>Configure maximum duration and inactivity in minutes for each user under Session limits. Defaults are 720 minutes maximum and 60 minutes inactivity. Zero disables the corresponding plugin limit; native WordPress expiration still applies. Existing _session_time_limit values remain maximum-duration settings.<\/p>\n\n<p>Each browser\/device keeps its own existing WordPress token. Expiring one token preserves the user's other valid tokens. The plugin never creates authentication tokens or synthesizes wp_login\/wp_logout events.<\/p>\n\n<p>A local script warns during the final five minutes (or immediately for shorter sessions). Actual keyboard, pointer, touch and scroll input can renew inactivity via authenticated, nonce-protected AJAX. Background Heartbeat and polling do not renew inactivity. Multiple tabs in the same login share an activity marker.<\/p>\n\n<p>Server checks reject expired cookie-authenticated requests even without JavaScript. WP-Cron automatically scans up to 200 users per run every fifteen minutes. Scans advance a persistent cursor; large user populations require multiple runs. MySQL\/MariaDB advisory locks prevent overlapping sweeps and serialize this plugin's per-user changes. Compare-and-swap metadata writes protect concurrent token changes.<\/p>\n\n<p>IMPORTANT: WP-Cron depends on traffic or an external trigger. It cannot guarantee logout at the exact deadline on an inactive site. A running browser timer is also subject to browser suspension and throttling. Use system cron for more reliable scheduling, and size the batch\/frequency for the site's user count.<\/p>\n\n<h4>WP-CLI \/ system cron<\/h4>\n\n<p>wp --path=\/absolute\/path\/to\/wordpress dh-usl sweep --batch=200<\/p>\n\n<p>Run this from the operating system scheduler as the appropriate site account. The batch is clamped to 10..1000 users. A busy run reports busy:true without doing work. System cron remains supported alongside WP-Cron; both use the same sweep and lock. Even system cron is subject to scheduling delay, batch coverage and server availability.<\/p>\n\n<h4>Integrations<\/h4>\n\n<p>Formerly DH User Session Limiter and DD User Session Limiter. The public name, directory slug and translation domain are now Dalmatian Dream User Session Limiter \/ dalmatian-dream-user-session-limiter. Existing dh_usl_* hooks, AJAX actions, session fields, options, user metadata and the dh-usl CLI command intentionally retain their identifiers to preserve settings and integrations.<\/p>\n\n<p>The existing dh_usl_session_expired action receives four arguments: integer user ID, SHA-256 token verifier, reason (INACTIVITY, MAX_DURATION or NATIVE_SESSION), and Unix timestamp. No raw token is emitted. Events follow confirmed removal. A second sweep does not repeat an event. This is an at-most-once best effort notification, not a durable message queue: a process crash after removal can lose a notification.<\/p>\n\n<p>All In One WP Security renamed login configuration is respected. Its delete_logged_in_user method is called only when no valid WordPress sessions remain. Because AIOWPS records are user-level, records may remain while another device is active. No artificial login\/logout events are fired.<\/p>\n\n<h4>Requirements and scope<\/h4>\n\n<p>Single-site WordPress only; on multisite the plugin remains inert and displays an administrator notice (activation does not produce a fatal error). Uses the default WP_User_Meta_Session_Tokens backend and MySQL 5.7.5+ or MariaDB 10.0.2+ supporting multiple named GET_LOCK\/RELEASE_LOCK locks per connection. Custom session managers and SQLite are not supported. Cookie sessions are covered; Application Passwords are a different authentication mechanism and have no browser session to expire. Admin and subscriber sessions follow the same deadlines.<\/p>\n\n<h4>Privacy<\/h4>\n\n<p>No external services, telemetry, remote assets, or plugin-originated outbound network requests. Activity requests go only to this site's admin-ajax.php. The plugin stores _dh_usl_max, _dh_usl_idle and legacy _session_time_limit per-user settings and dh_usl_started\/dh_usl_last timestamps in existing WordPress session metadata. WordPress itself manages tokens, login time, IP and user-agent data. A sweep cursor is stored in options. A session-scoped HMAC activity key and most recent interaction timestamp are stored in browser localStorage; this is not sent to third parties. Old localStorage markers may remain until site data is cleared. Signed finish URLs contain user ID, a token hash, deadline and signature; servers may record request URLs. They contain no raw token and cannot clear a different browser login. The plugin adds no debug log records. Integration listeners may process expiry events under the site owner's responsibility.<\/p>\n\n<p>Deactivation removes scheduled cleanup and its cursor. Limits and session timestamps are preserved for reactivation. Deleting the plugin also preserves settings; WordPress user deletion removes user metadata. Administrators can delete the three per-user limit keys when permanent removal is wanted. Clear browser site data to remove activity markers. Existing session tokens remain managed by WordPress.<\/p>\n\n<!--section=installation-->\n<ol>\n<li>Back up the site and test with your integrations on staging.<\/li>\n<li>Upload the ZIP via Plugins &gt; Add Plugin &gt; Upload Plugin, or extract the dalmatian-dream-user-session-limiter folder into wp-content\/plugins.<\/li>\n<li>Activate on a single-site WordPress installation. A recurring cleanup event is registered automatically.<\/li>\n<li>Open Session limits as an administrator. Set maximum and idle minutes per user and save the displayed page.<\/li>\n<li>Verify separate browser logins and your notification listeners. Configure system cron when reliable scheduled cleanup is needed.<\/li>\n<\/ol>\n\n<p>When replacing an older manually installed folder named DH-user-session-limiter, dh-user-session-limiter or dd-user-session-limiter, deactivate it first and replace it with dalmatian-dream-user-session-limiter. Do not run both copies simultaneously. User settings are preserved.<\/p>\n\n<!--section=faq-->\n<dl>\n<dt id=\"does%20inactivity%20mean%20any%20http%20traffic%3F\"><h3>Does inactivity mean any HTTP traffic?<\/h3><\/dt>\n<dd><p>No. The browser script sends activity after real interaction. Server-only clients without this script do not renew idle timestamps automatically.<\/p><\/dd>\n<dt id=\"can%20an%20inactive%20site%20log%20out%20users%20at%20the%20exact%20second%3F\"><h3>Can an inactive site log out users at the exact second?<\/h3><\/dt>\n<dd><p>No. Access is denied when a subsequent authenticated request is checked; background token cleanup depends on cron execution and batch coverage.<\/p><\/dd>\n<dt id=\"does%20one%20device%20logout%20log%20out%20all%20devices%3F\"><h3>Does one device logout log out all devices?<\/h3><\/dt>\n<dd><p>No. The plugin removes only the expired session token. Other WordPress plugins may implement their own global logout policies.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>2.3.9<\/h4>\n\n<ul>\n<li>Updated public name, directory slug and translation domain to Dalmatian Dream User Session Limiter.<\/li>\n<li>Multisite activation no longer fails with wp_die; unsupported multisite installations remain inert and show an administrator notice.<\/li>\n<li>Cleanup scheduling failures now show an administrator notice instead of terminating activation.<\/li>\n<li>Preserved legacy session metadata, hooks, AJAX actions, CLI command and user settings.<\/li>\n<\/ul>\n\n<h4>2.3.8<\/h4>\n\n<ul>\n<li>Added bounded WP-Cron cleanup with activation\/deactivation lifecycle and shared sweep locking.<\/li>\n<li>Hardened expiry enforcement, signed finish links, REST authentication and concurrent metadata writes.<\/li>\n<li>Removed sensitive tracing; added translations, GPL license and operator documentation.<\/li>\n<\/ul>\n\n<h4>2.3.7<\/h4>\n\n<ul>\n<li>Original supplied release: per-session limits, activity warnings, AIOWPS adapter and CLI sweep.<\/li>\n<\/ul>","raw_excerpt":"Limit maximum login duration and inactivity independently for each WordPress browser session, with warnings and bounded server-side cleanup.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/399210","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=399210"}],"author":[{"embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/whitedd"}],"wp:attachment":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=399210"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=399210"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=399210"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=399210"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=399210"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=399210"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}