RouterOS Hotspot: Walled Garden, Custom Splash Page, and Access Policies
RouterOS Hotspot: Walled Garden, Custom Splash Page, and Access Policies
Section titled “RouterOS Hotspot: Walled Garden, Custom Splash Page, and Access Policies”| Configuration Guide | md-7law |
|---|---|
| Date | March 22, 2026 |
| RouterOS Version | 7.x |
Overview
Section titled “Overview”This guide covers advanced Hotspot configuration beyond basic setup:
- Walled garden — allow specific hosts or IPs before login
- Custom splash page — replace the built-in login HTML with branded templates
- HTTPS redirect — enforce TLS on the login page
- Rate limits — cap per-profile and per-user bandwidth
- Trial authentication — grant time-limited access without credentials
- Voucher authentication — distribute pre-created username/password credentials
For initial Hotspot setup (wizard, server profile, DHCP pool) see the Hotspot Captive Portal Gateway Guide.
Walled Garden
Section titled “Walled Garden”The walled garden defines what unauthenticated clients can reach before logging in. RouterOS provides two separate tables matched in order:
| Table | Path | Match layer |
|---|---|---|
| HTTP walled garden | /ip hotspot walled-garden | HTTP Host header + URL path (L7) |
| IP walled garden | /ip hotspot walled-garden ip | IP, protocol, port (L3/L4) |
Rules in each table are evaluated top-to-bottom; the first match wins.
HTTP walled garden
Section titled “HTTP walled garden”Use for plain-HTTP destinations matched by hostname or path. Wildcards (*) are supported in dst-host and dst-path.
/ip hotspot walled-garden
# Allow entire domain (HTTP only)add dst-host=example.com action=accept comment="Registration portal"
# Allow wildcard subdomainadd dst-host=*.example.com action=accept comment="All subdomains of example.com"
# Allow specific path onlyadd dst-host=pay.example.com dst-path=/checkout* \ action=accept comment="Payment checkout page"
# Explicitly deny a host (useful to override a broad wildcard)add dst-host=ads.example.com action=deny comment="Block ad subdomain"HTTPS limitation: The HTTP walled garden cannot match HTTPS destinations by hostname alone — TLS prevents inspection of the
Hostheader without termination. Use the IP walled garden for HTTPS resources.
IP walled garden
Section titled “IP walled garden”Use for HTTPS destinations, non-HTTP protocols, and CDN backend IPs. Rules match destination address, protocol, and port independently.
/ip hotspot walled-garden ip
# Allow HTTPS to a specific IPadd dst-address=198.51.100.10 protocol=tcp dst-port=443 \ action=accept comment="Payment gateway HTTPS"
# Allow all traffic to a subnet (e.g. local NTP server)add dst-address=203.0.113.0/24 action=accept \ comment="Internal services subnet"
# Allow DNS to a specific resolveradd dst-address=1.1.1.1 protocol=udp dst-port=53 \ action=accept comment="Allow Cloudflare DNS pre-auth"
# Allow OS captive-portal detection endpointsadd dst-address=204.79.197.200 protocol=tcp dst-port=80 \ action=accept comment="Microsoft NCSI detection"add dst-address=142.250.0.0/15 protocol=tcp dst-port=80 \ action=accept comment="Google Captive Portal detection"Property reference
Section titled “Property reference”HTTP walled garden (/ip hotspot walled-garden):
| Property | Description |
|---|---|
dst-host | HTTP Host header to match; supports * wildcard |
dst-path | URL path to match; supports * wildcard |
method | HTTP method (GET, POST, etc.); omit to match all |
action | accept or deny |
comment | Optional description |
IP walled garden (/ip hotspot walled-garden ip):
| Property | Description |
|---|---|
dst-address | Destination IP or subnet |
src-address | Source IP or subnet (restricts which clients the rule applies to) |
protocol | tcp, udp, icmp, or blank for any |
dst-port | Destination port or range (requires protocol) |
action | accept, reject, or drop |
Verifying walled garden rules
Section titled “Verifying walled garden rules”# Show HTTP walled garden entries/ip hotspot walled-garden print
# Show IP walled garden entries/ip hotspot walled-garden ip print
# Monitor active hotspot hosts (including unauthenticated)/ip hotspot host printCustom Splash Page (Login Page Customisation)
Section titled “Custom Splash Page (Login Page Customisation)”RouterOS serves login pages from HTML template files. You can replace or modify these files to create a branded captive portal experience.
Template files
Section titled “Template files”The built-in templates live on the router’s flash storage under /hotspot/. The files most commonly customised:
| File | Shown when |
|---|---|
login.html | Unauthenticated client is redirected to log in |
logout.html | User clicks logout |
status.html | Authenticated user opens the portal status page |
error.html | Authentication fails |
alogin.html | Auto-login redirect after successful authentication |
rlogin.html | Redirect for clients needing login (alternative flow) |
radvert.html | Post-login advertisement or redirect |
Files not present in a custom directory fall back to the built-in defaults.
Servlet template variables
Section titled “Servlet template variables”RouterOS injects variables at render time using $(variable) syntax. These must be preserved in any custom template for authentication to work.
Authentication form variables:
| Variable | Description |
|---|---|
$(link-login-only) | Form action URL — always use this, not a hardcoded path |
$(link-orig) | Original URL the client was trying to reach (for post-login redirect) |
$(username) | Pre-filled username (from cookie or MAC bypass) |
$(chap-id) | CHAP challenge ID (HTTP CHAP auth) |
$(chap-challenge) | CHAP challenge value (HTTP CHAP auth) |
Display variables:
| Variable | Description |
|---|---|
$(error) | Error message string after failed login attempt |
$(ip) | Client IP address |
$(mac) | Client MAC address |
$(session-time-left) | Remaining session time for authenticated users |
$(uptime) | Session uptime |
Conditional blocks:
$(if error)<p class="error">$(error)</p>$(endif)$(if username)<p>Welcome back, $(username)</p>$(endif)Minimal login.html template
Section titled “Minimal login.html template”This is a stripped-down but fully functional login page. All hidden fields are required.
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Network Login</title> <style> body { font-family: sans-serif; max-width: 400px; margin: 60px auto; padding: 1rem; } .error { color: #c00; } input[type=text], input[type=password] { width: 100%; padding: 8px; margin: 4px 0 12px; } button { width: 100%; padding: 10px; background: #0068c9; color: #fff; border: none; cursor: pointer; } </style></head><body> <h2>Sign in to continue</h2> $(if error)<p class="error">$(error)</p>$(endif) <form action="$(link-login-only)" method="post" accept-charset="UTF-8"> <input type="hidden" name="chap-id" value="$(chap-id)"> <input type="hidden" name="chap-challenge" value="$(chap-challenge)"> <input type="hidden" name="dst" value="$(link-orig)"> <label>Username<br> <input type="text" name="username" value="$(username)" autocomplete="username"> </label> <label>Password<br> <input type="password" name="password" autocomplete="current-password"> </label> <button type="submit">Log In</button> </form></body></html>Deploying a custom directory
Section titled “Deploying a custom directory”-
Create the directory and upload your files via Winbox → Files, FTP, or SCP:
# Files uploaded to flash:/hotspot_custom/login.html etc. -
Point the server profile at the custom directory:
/ip hotspot profileset [find name=hsprof1] html-directory-override=hotspot_custom -
Verify the profile is updated:
/ip hotspot profile print where name=hsprof1
Partial override: Only files present in the custom directory are used; missing files fall back to the built-in defaults. You can ship only
login.htmland leave the rest as defaults.
HTTPS Redirect
Section titled “HTTPS Redirect”Enabling HTTPS redirect sends clients to the login page over TLS, preventing credential exposure on the captive portal login.
Certificate requirement
Section titled “Certificate requirement”A certificate with a CN matching the hotspot dns-name is required. Generate a self-signed certificate if no public certificate is available:
/certificateadd name=hotspot-ca common-name=hotspot-ca key-size=2048 \ days-valid=3650 key-usage=key-cert-sign,crl-signsign hotspot-ca ca=hotspot-ca
add name=hotspot-cert common-name=login.example.com key-size=2048 \ days-valid=3650 subject-alt-name=IP:10.5.50.1sign hotspot-certEnable HTTPS in the server profile
Section titled “Enable HTTPS in the server profile”/ip hotspot profileset [find name=hsprof1] \ ssl-certificate=hotspot-cert \ login-by=https,http-chap,cookie| Property | Values | Description |
|---|---|---|
ssl-certificate | certificate name | Certificate used for the HTTPS login page |
login-by | comma-separated list | Include https to accept TLS-authenticated logins |
Behaviour notes
Section titled “Behaviour notes”- Clients visiting HTTP destinations are intercepted and redirected to
https://$(dns-name)/login. If the certificate is self-signed, the browser shows a trust warning. - HTTPS destinations (client’s initial request) cannot be intercepted and redirected by the Hotspot engine — the TLS handshake completes to the original server before RouterOS can inject a redirect. Use walled-garden IP rules to allow OS captive-portal detection endpoints so that HTTP-based probes still trigger the portal.
- For production deployments, use a certificate signed by a public CA (obtained via Let’s Encrypt with a domain that points to the router’s WAN or a publicly reachable IP).
User Profile Rate Limits
Section titled “User Profile Rate Limits”User profiles (/ip hotspot user profile) define bandwidth policy applied to authenticated sessions. Multiple profiles allow service tiers.
Rate limit syntax
Section titled “Rate limit syntax”rx/tx[burst-rx/burst-tx[burst-threshold-rx/burst-threshold-tx[burst-time-rx/burst-time-tx[priority[min-rx/min-tx]]]]]Units: k = kbit/s, M = Mbit/s, G = Gbit/s. rx is download (router→client), tx is upload (client→router).
Common examples:
5M/2M # 5 Mbit/s download, 2 Mbit/s upload, no burst10M/5M 20M/10M # 10/5 sustained, burst to 20/1010M/5M 20M/10M 8M/4M 10s # burst above 8/4 threshold for 10 secondsConfiguring profiles
Section titled “Configuring profiles”/ip hotspot user profile
# Free tier: 2 Mbit/s symmetric, 30 minute sessionsadd name=free \ rate-limit=2M/2M \ session-timeout=30m \ idle-timeout=10m \ shared-users=1
# Basic tier: 10 Mbit/s down, 5 up, 8h sessionsadd name=basic \ rate-limit=10M/5M \ session-timeout=8h \ idle-timeout=30m \ shared-users=1
# Premium tier: 50 Mbit/s with burst, unlimited sessionadd name=premium \ rate-limit="50M/20M 100M/40M 40M/16M 10s" \ session-timeout=0 \ idle-timeout=1h \ shared-users=3Note:
rate-limitis a profile-level property only. It cannot be set on individual/ip hotspot userentries. To give a specific user a different bandwidth cap, assign them to a dedicated profile with the desired rate limit.
Key user profile properties
Section titled “Key user profile properties”| Property | Description |
|---|---|
rate-limit | Bandwidth cap; overridden by user-level rate-limit |
session-timeout | Maximum session duration; 0 = unlimited |
idle-timeout | Disconnect after idle; 0 = disabled |
shared-users | Max simultaneous logins with the same credentials |
address-pool | IP pool for clients using this profile |
incoming-filter | Firewall chain applied to inbound client traffic |
outgoing-filter | Firewall chain applied to outbound client traffic |
Trial Authentication
Section titled “Trial Authentication”Trial authentication allows clients to connect for a limited time without providing credentials. The client clicks a “Try for free” button (or the portal auto-submits) and receives a session governed by the trial profile.
Configure trial in the server profile
Section titled “Configure trial in the server profile”/ip hotspot user profileadd name=trial \ rate-limit=1M/1M \ session-timeout=30m \ shared-users=1
/ip hotspot profileset [find name=hsprof1] \ login-by=http-chap,http-pap,cookie,trial \ trial-uptime-limit=30m \ trial-uptime-reset=1d \ trial-user-profile=trial| Property | Description |
|---|---|
login-by=trial | Enables the trial login method for this server profile |
trial-uptime-limit | Maximum trial session duration per reset period (e.g. 30m, 1h) |
trial-uptime-reset | How often the trial uptime counter resets per client (e.g. 1d) — resets at midnight by default |
trial-user-profile | User profile applied to trial sessions; controls rate limit and timeouts |
Adding a trial button to login.html
Section titled “Adding a trial button to login.html”Add a second form that POSTs username=T- + client MAC to trigger the trial flow:
<!-- Trial / Guest access form --><form action="$(link-login-only)" method="post" accept-charset="UTF-8"> <input type="hidden" name="dst" value="$(link-orig)"> <input type="hidden" name="username" value="T-$(mac)"> <input type="hidden" name="password" value="$(mac)"> <button type="submit">Try for 30 minutes (no login required)</button></form>RouterOS recognises the
T-<mac>username pattern and maps it to the trial profile defined in the server profile. The trial counter is tracked per MAC address.
Trial uptime tracking
Section titled “Trial uptime tracking”Trial usage is tracked in the active host table. To inspect remaining trial time:
/ip hotspot host print where comment~"trial"/ip hotspot active printVoucher Authentication
Section titled “Voucher Authentication”Voucher authentication is standard username/password authentication where the credentials are pre-generated and printed or distributed as vouchers. RouterOS has no separate “voucher” object — a voucher is simply a Hotspot user account.
Create voucher users
Section titled “Create voucher users”/ip hotspot user
# Single voucher — 1 hour, 1 deviceadd name=VCHR-0001 password=X7kP2m profile=basic \ limit-uptime=1h shared-users=1 comment="Day pass voucher"
# Bulk creation via script:for i from=1 to=50 do={ :local name ("VCHR-" . [:tostr $i]) :local pass [/certificate scep-server generate-key] /ip hotspot user add name=$name password=$pass \ profile=basic limit-uptime=1h shared-users=1}Key per-user properties for vouchers
Section titled “Key per-user properties for vouchers”| Property | Description |
|---|---|
limit-uptime | Total connected time before account expires (e.g. 1h, 1d) |
limit-bytes-total | Total data quota (upload + download) before expiry |
limit-bytes-in | Download quota |
limit-bytes-out | Upload quota |
shared-users | Max simultaneous sessions; set to 1 for single-device vouchers |
disabled | Disable the account without deleting it |
Voucher expiry behaviour
Section titled “Voucher expiry behaviour”When a voucher reaches its limit-uptime or byte quota:
- The active session is immediately terminated.
- The user account is not automatically deleted — it remains in the user list in an exhausted state.
- Subsequent login attempts with the same credentials are rejected.
To clean up used vouchers:
# Remove users whose uptime limit is exhausted/ip hotspot user remove [find limit-uptime>0 uptime>=limit-uptime]User Manager integration for vouchers
Section titled “User Manager integration for vouchers”For larger deployments, User Manager provides a dedicated voucher generation UI and centrally manages quotas via RADIUS. See the User Manager guide for setup, then point the Hotspot server at User Manager:
/radiusadd service=hotspot address=127.0.0.1 secret=shared-secret \ authentication-port=1812 accounting-port=1813
/ip hotspot profileset [find name=hsprof1] use-radius=yesVerification
Section titled “Verification”# Active authenticated sessions/ip hotspot active print
# All hosts, including unauthenticated and trial/ip hotspot host print
# HTTP walled garden rules/ip hotspot walled-garden print
# IP walled garden rules/ip hotspot walled-garden ip print
# User profile list/ip hotspot user profile print
# Check hotspot server profile settings/ip hotspot profile print detail where name=hsprof1