The branded "accept the terms and connect" page every guest expects — and every hotel owner asks us for. This is the MikroTik hotspot done properly: a login page you can put a logo on, voucher codes at reception, optional RADIUS, a walled garden so the login itself always loads, per-guest bandwidth, and the HTTPS-redirect reality nobody warns you about.
A captive portal is a small, well-worn trick: the router runs a tiny web server, and until a device authenticates, every HTTP request it makes is redirected to the login page. Accept the terms (or enter a voucher), the router flips that device's MAC to "authorised," and traffic flows normally. Phones recognise the pattern and pop the page automatically — that's the "Sign in to Wi-Fi" sheet every guest has seen.
On MikroTik this is the hotspot feature, and it's genuinely good — a five-minute wizard gets you a working portal, and the login page is just editable HTML you can brand. This post takes it from that wizard to something you'd actually hand a hotel: a terms-acceptance page, voucher codes printed at reception via User Manager, an optional RADIUS backend, the walled garden that keeps the portal itself reachable, and per-guest speed limits. It's the guest-facing front door to the segmented network from the hotel lab.
01
The setup command wires the whole thing — an IP pool, DHCP, a NAT rule, the portal server, and a default profile — onto one interface. Run it on the guest interface, never the WAN.
# Point it at the guest interface (a VLAN interface or a guest bridge). # The wizard asks a handful of questions — accept the sensible defaults. [admin@GW] > /ip hotspot setup hotspot interface: vlan20-guest local address of network: 10.10.20.1/24 (auto) masquerade network: yes address pool: 10.10.20.10-10.10.20.254 select certificate: none ip address of smtp server: 0.0.0.0 dns servers: 10.10.20.1 dns name: wifi.hotel.local name of local user: (skip — we use vouchers) # What it created, in one place: [admin@GW] > /ip hotspot print 0 name="hotspot1" interface=vlan20-guest address-pool=hs-pool-guest profile=hsprof1 idle-timeout=5m keepalive-timeout=none
.local-ish
The dns name (here wifi.hotel.local) is the hostname the portal answers on — guests are redirected to http://wifi.hotel.local/login. Pick something that clearly isn't a real public domain, and never use your actual website's name (the portal would shadow it for every connected guest). It also must resolve only inside the hotspot — the wizard's built-in DNS handles that. If you later add HTTPS to the portal, this name must match the certificate (see the HTTPS gotcha in section 06).
02
The simplest hotel portal isn't a username box — it's a logo, a "I accept the terms" checkbox, and a Connect button. That's a two-line change to the login profile plus a branded HTML file.
# "trial" login lets a guest on for a set time with no credentials — # perfect for accept-and-connect. Give everyone the same shared voucher-free access. [admin@GW] > /ip hotspot user profile set [find name=default] \ shared-users=1 rate-limit=10M/40M session-timeout=1d idle-timeout=15m [admin@GW] > /ip hotspot profile set [find name=hsprof1] \ login-by=cookie,http-chap trial-uptime-limit=1d trial-uptime-reset=1d # cookie login remembers a returning guest's device for the day so they # don't re-accept on every reconnect (the MAC-cookie in section 06).
The portal's HTML lives on the router's flash, in the hotspot/ directory — login.html, alogin.html (the "you're connected" page), logout.html, and a few includes. Pull them off with WinBox's Files browser or SFTP, edit them, and drop them back. For an accept-and-connect page you keep MikroTik's login form but replace the username/password inputs with a single checkbox and a hidden trial submit:
<img src="logo.png" alt="Hotel">
<h1>Welcome — free guest Wi-Fi</h1>
<form name="login" action="$(link-login-only)" method="post">
<input type="hidden" name="dst" value="$(link-orig)">
<input type="hidden" name="popup" value="true">
<label><input type="checkbox" required>
I accept the <a href="/terms.html">terms of use</a></label>
<!-- "trial" hands out the free timed session; no username needed -->
<input type="submit" name="" value="Connect">
</form>
<!-- MikroTik replaces $(...) macros server-side before serving the page -->
The guest isn't online yet when they see this page, so every asset it references must be on the router: the logo, CSS, fonts, any image. Link a Google Font or a CDN logo and it renders broken, because the guest can't reach the internet until after they submit. Put logo.png and a small style.css in the same hotspot/ folder and reference them relatively. This is the single most common reason a "custom portal" looks broken on the guest's phone.
03
When "free for everyone" isn't the model — paid Wi-Fi, per-room codes, a code printed on the checkout receipt — you need vouchers. User Manager is MikroTik's built-in RADIUS server that generates and tracks them.
# RouterOS 7 ships User Manager v5 as a package — enable it once. [admin@GW] > /user-manager set enabled=yes # Register the router itself as a RADIUS client of its own User Manager [admin@GW] > /user-manager router add name=local address=127.0.0.1 shared-secret=hotspot-secret # Tell the hotspot to authenticate against RADIUS instead of local users [admin@GW] > /ip hotspot profile set [find name=hsprof1] use-radius=yes [admin@GW] > /radius add service=hotspot address=127.0.0.1 secret=hotspot-secret # A profile that shapes every voucher user, then a batch of 50 codes [admin@GW] > /user-manager profile add name=guest-1day validity=1d [admin@GW] > /user-manager user create-and-activate-profile=guest-1day \ count=50 name-length=6 shared-users=1 # Print the batch to hand to reception: [admin@GW] > /user-manager user print where profile=guest-1day # NAME PASSWORD PROFILE 0 a7f3k9 a7f3k9 guest-1day 1 m2x8qp m2x8qp guest-1day …
A common panic: "I made 50 one-day codes last month, are they all expired?" No — with User Manager v5, a voucher's validity countdown begins the moment it's first used, not when it's generated. So a stack of unused codes at reception stays good indefinitely; only handed-out ones burn down. Confirm the intended behaviour with a test code before a busy weekend — the difference between "validity" and "session-timeout" trips up everyone the first time, and getting it wrong means either codes that die on the shelf or codes that never expire.
04
Some traffic must pass before login: the portal's own assets, a payment provider if you sell access, captive-portal detection URLs so phones auto-pop the page. That allow-list is the walled garden.
# Payment gateway (if guests pay by card for access) [admin@GW] > /ip hotspot walled-garden add dst-host=*.stripe.com action=allow # Captive-portal detection endpoints — so iOS/Android reliably auto-open # the login sheet instead of silently deciding "no internet" [admin@GW] > /ip hotspot walled-garden add dst-host=captive.apple.com action=allow [admin@GW] > /ip hotspot walled-garden add dst-host=connectivitycheck.gstatic.com action=allow [admin@GW] > /ip hotspot walled-garden add dst-host=www.msftconnecttest.com action=allow # For raw IPs / non-HTTP, use the IP walled garden instead [admin@GW] > /ip hotspot walled-garden ip add dst-address=1.1.1.1 action=accept
Every walled-garden entry is free, unauthenticated internet for anyone in Wi-Fi range — so keep it to detection URLs and (if used) the payment host. We've audited "free Wi-Fi" hotspots whose walled garden had grown to include social media and streaming "so guests could preview" — which is just open Wi-Fi with extra steps, and on a metered or capped line, a bill. The garden is a short list; if it's long, something's wrong with the model.
05
The hotspot has its own rate-limit that composes with — and should defer to — the PCQ fairness from the QoS post. Set a per-session cap here; let PCQ share the pool.
# rate-limit on the user profile: rx/tx from the GUEST's perspective. # 10M down / 5M up per device is plenty for a hotel guest. [admin@GW] > /ip hotspot user profile set [find name=default] rate-limit=5M/10M # Advanced rate-limit syntax also supports burst — generous for a few # seconds (snappy page loads) then settles to the sustained cap: # rate-limit=5M/10M 8M/16M 6M/12M 8 8 8 (rx/tx burst threshold time) # Then let PCQ (from the QoS post) share the total guest pool fairly on # top of the per-device cap — the two layers stack: [admin@GW] > /queue simple add name=guest-pool target=10.10.20.0/24 \ max-limit=300M/300M queue=pcq-up/pcq-down
The same trap as the firewall post: if a FastTrack rule matches guest traffic, the hotspot's shaping (and your PCQ queues) never see the packets, and every "limit" silently does nothing. On a box running a guest hotspot, scope FastTrack to exclude the guest subnet, or don't FastTrack at all. Symptom: you set 10M per guest, one guest pulls 400M, and the queue counters read zero. The packets are being fast-pathed around the shaper.
06
Captive portals were designed for a plaintext-HTTP web. The modern all-HTTPS web breaks the classic "redirect any page" trick — here's what actually happens on a guest's phone, and how to make it smooth.
A hotspot works by intercepting an HTTP request and answering with a redirect. But browsers now default to HTTPS, and the router cannot transparently intercept an HTTPS request — doing so would be a certificate error, which is exactly what HTTPS exists to prevent. So if a guest's first action is opening https://…, they get a connection failure, not your portal.
What saves you is captive-portal detection: every modern phone, on joining Wi-Fi, quietly fetches a known plaintext URL (Apple's captive.apple.com, Android's connectivitycheck.gstatic.com). If it gets your redirect instead of the expected response, it pops the login sheet automatically. So the fix isn't fighting HTTPS — it's making sure those detection URLs are handled and not accidentally allowed through the walled garden (allow them to the portal, don't let them bypass it).
| Symptom in the field | Cause & fix |
|---|---|
| "No internet" but no login page | Detection URL blocked or mis-handled — ensure the walled garden allows the detection hosts to the portal, not around it. |
| Login page loads broken / no logo | Portal assets referenced from a CDN — put logo/CSS on the router (section 02). |
| Guest re-accepts on every reconnect | Enable login-by=cookie so the device MAC is remembered for the session (section 02). |
| Portal throws a cert warning | You enabled HTTPS login without a cert whose CN matches the dns name. Either use plain HTTP for the portal (fine on an isolated guest VLAN) or install a matching cert. |
| Some devices never see the portal | They opened an HTTPS page first and cached it. Toggle Wi-Fi off/on, or open http://neverssl.com — the reception-desk trick. |
For most small properties: an accept-and-connect portal on plain HTTP (the guest VLAN is already isolated, so there's no credential to steal), cookie login so returning guests skip the page, a two-line walled garden, and a per-guest cap under PCQ. Vouchers only where the owner wants paid or per-room access. We keep the portal's HTML in the config backup so a reflash restores the branding too — the logo on that page is often the only thing the owner personally checks after we hand over.
07
Cisco's equivalent lives on the wireless controller, not the router — worth knowing if a site is mixed.
| MikroTik / RouterOS | Cisco |
|---|---|
| /ip hotspot (built into the router) | WLC guest anchor + web-auth policy (on the controller) |
| Editable login.html on flash | Custom web-auth bundle uploaded to the WLC |
| User Manager vouchers | ISE guest sponsor portal / local web-auth accounts |
| Walled garden | Pre-auth ACL on the guest WLAN |
| use-radius=yes → User Manager | RADIUS to ISE / a NAC |
| rate-limit on the user profile | QoS profile / rate limit per WLAN |
Takeaways
/ip hotspot setup builds the whole stack on one guest interface.NOCTIS builds captive portals for hotels and villas across Crete — your logo, terms acceptance or vouchers, isolated from your real network, fair in high season, and tested on the phones your guests actually carry.
Book a Discovery Call →