๐ Objective
Create a wireless hotspot network where users can sign up or log in via a custom splash page hosted on an external Linux web server, which integrates with a FreeRADIUS server for authentication.
๐ง 1. Infrastructure Overview
Devices and Services:
Component | Role | IP Address |
---|---|---|
MikroTik Router | Hotspot gateway and CAPsMAN controller | WAN 10.2.0.6 LAN (GUEST) 10.0.30.1/24 |
Web Server (Linux) | Hosts login.html , signup.php , style.css | 10.2.0.9 |
DNS Hostname | Splash page hostname | hotspot.mikrotikmasters.com |
FreeRADIUS | Authentication backend (not covered here) | 10.2.0.9 |
NB: FreeRADIUS and Login server running on the same Ubuntu Server
To test logging a user in you can put this URL in the browser:
http://<router-ip-or-hostname>/login?username=<username>&password=<password>
๐ 2. DNS and Host Resolution
To ensure users get redirected correctly to the splash page, the MikroTik hotspot system resolves:
hotspot.mikrotikmasters.com โ 10.2.0.6
login.mikrotikmasters.comโ 10.2.0.
9
On the Web Server:
Edit /etc/hosts
:
sudo nano /etc/hosts
Add:
10.2.0.6 hotspot.mikrotikmasters.com
This ensures PHP CURL requests from the server can resolve the hostname during login.
On the mikroTik
/ip dns static
add address=10.2.0.9 name=login.mikrotikmasters.com type=A
๐ก 3. Hotspot Configuration
MikroTik config:
- Hotspot is configured on a VLAN/subinterface (e.g.,
vlan30::GUEST
) - Uses a DNS name:
hotspot.mikrotikmasters.com
- The login page is hosted externally (
index.html
not on router) - RADIUS authentication is active for the hotspot
- CAPsMAN is used to control access points with a virtual AP broadcasting the Hotspot SSID
Hotspot Redirect
In order to get the hotspot to redirect to your login server, we need to replace the existing hotspot/login.html file with this:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="refresh" content="0; url=http://login.mikrotikmasters.com/login/">
</head>
<body>
<p>Redirecting...</p>
</body>
</html>

๐งพ 4. Web Portal Files and Functions
๐น index.php
- This is the custom splash page shown to users when they connect to WiFi.
- It has:
- White background
- Login form inside a centered box (440 x 415px)
- Rounded edges, logo at top (
img/login-logo.png
) - Responsive for mobile/tablet
- Action: sends POST to
http://hotspot.mikrotikmasters.com/login
(MikroTik router) - Optional button: links to
signup.php
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>WiFi Login</title>
<link rel="stylesheet" href="style.css">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<?php
$prefill_username = isset($_GET['username']) ? htmlspecialchars($_GET['username']) : '';
$prefill_password = isset($_GET['password']) ? htmlspecialchars($_GET['password']) : '';
$link_login = $_GET['link-login'] ?? 'http://hotspot.mikrotikmasters.com/login';
$dst = $_GET['dst'] ?? 'http://example.com';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$username = $_POST['username'];
$password = $_POST['password'];
$data = [
'username' => $username,
'password' => $password
];
$ch = curl_init($link_login);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
$response = curl_exec($ch);
curl_close($ch);
// Optional: check for login success in $response
header("Location: $dst");
exit;
}
?>
<body>
<div class="login-box">
<img src="img/login-logo.png" alt="Logo" class="logo">
<form method="post" action="http://hotspot.mikrotikmasters.com/login">
<input type="text" name="username" placeholder="Username" value="<?= $prefill_username ?>" required>
<input type="password" name="password" placeholder="Password" value="<?= $prefill_password ?>" required>
<input type="submit" value="Login">
</form>
<form method="get" action="signup.php">
<input type="submit" value="Sign Up">
</form>
</div>
</body>
</html>
๐น style.css
- Centralised styling for
login.html
andsignup.php
- Ensures consistent appearance for:
- Logo
- Form width
- Buttons
- Box shadow, rounded borders, spacing
body {
margin: 0;
padding: 0;
background: white;
font-family: Arial, sans-serif;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
.login-box {
width: 90%;
max-width: 440px;
height: auto;
padding: 20px;
border: 1px solid #555;
border-radius: 10px;
box-sizing: border-box;
text-align: center;
}
.logo {
width: 315px;
margin-bottom: 20px;
}
form {
margin: 10px 0;
}
input[type="text"],
input[type="email"],
input[type="password"],
input[type="submit"] {
width: 100%;
padding: 12px;
margin: 8px 0;
box-sizing: border-box;
font-size: 16px;
}
input[type="submit"] {
background-color: #333;
color: white;
border: none;
cursor: pointer;
border-radius: 5px;
}
input[type="submit"]:hover {
background-color: #555;
}
๐น signup.php
A custom user registration handler. Steps:
- Form fields:
- Full Name
- Username
- Password
- Accept Terms (checkbox)
- Validation:
- Ensures no fields are empty
- Ensures terms are accepted
- Checks if the username already exists in the
radcheck
table
- Database Insertion:
- Inserts
name
andemail
into a customcustomers
table - Inserts
username
andpassword
intoradcheck
table for RADIUS authentication
- Inserts
- Auto-login:
- If signup succeeds, generates a hidden form POST to:
http://hotspot.mikrotikmasters.com/login
which logs the user in directly (no need to go to login page again)
- If signup succeeds, generates a hidden form POST to:
<?php
// Database config
$dbHost = 'localhost';
$dbUser = 'radius';
$dbPass = 'helloworld123';
$dbName = 'radius';
// Connect to MySQL
$conn = new mysqli($dbHost, $dbUser, $dbPass, $dbName);
if ($conn->connect_error) {
die("DB connection failed: " . $conn->connect_error);
}
$error = '';
$dst = isset($_GET['dst']) ? $_GET['dst'] : 'http://example.com';
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$name = trim($_POST['name']);
$email = trim($_POST['email']);
$username = trim($_POST['username']);
$password = trim($_POST['password']);
$terms = isset($_POST['terms']);
// Validate
if (!$name || !$email || !$username || !$password || !$terms) {
$error = "Please fill in all fields and accept the terms.";
} else {
// Check if username exists
$stmt = $conn->prepare("SELECT id FROM radcheck WHERE username = ?");
$stmt->bind_param("s", $username);
$stmt->execute();
$stmt->store_result();
if ($stmt->num_rows > 0) {
$error = "Username already exists. Please choose another.";
} else {
// Save to 'customers'
$stmt = $conn->prepare("INSERT INTO customers (name, email) VALUES (?, ?)");
$stmt->bind_param("ss", $name, $email);
$stmt->execute();
// Save to 'radcheck'
$stmt = $conn->prepare("INSERT INTO radcheck (username, attribute, op, value) VALUES (?, 'Cleartext-Password', ':=', ?)");
$stmt->bind_param("ss", $username, $password);
$stmt->execute();
$stmt->close();
$conn->close();
// Auto login form
echo "
<form id='autoLogin' method='post' action='http://hotspot.mikrotikmasters.com/login'>
<input type='hidden' name='username' value='" . htmlspecialchars($username) . "'>
<input type='hidden' name='password' value='" . htmlspecialchars($password) . "'>
<input type='hidden' name='dst' value='" . htmlspecialchars($dst) . "'>
</form>
<script>document.getElementById('autoLogin').submit();</script>";
exit;
}
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Signup</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="login-box">
<img src="img/login-logo.png" alt="Logo" class="logo">
<form method="post" action="signup.php">
<?php if ($error): ?>
<div class="error"><?= htmlspecialchars($error) ?></div>
<?php endif; ?>
<input type="text" name="name" placeholder="Full Name" required>
<input type="email" name="email" placeholder="Email" required>
<input type="text" name="username" placeholder="Username" required>
<input type="password" name="password" placeholder="Password" required>
<label style="display:block; font-size: 13px; margin: 5px 0;">
<input type="checkbox" name="terms" required> I accept the <a href="#">terms & conditions</a>
</label>
<input type="submit" value="Sign Up">
<a href="login.html"><button type="button">Back to Login</button></a>
</form>
</div>
</body>
</html>
โ MySQL Table Setup
Run this SQL on your radius
database:
CREATE TABLE customers (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
email VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
๐น PHP login submission (Auto POST)
Both signup.php
and index.php
eventually POST to:
http://hotspot.mikrotikmasters.com/login
MikroTik expects:
username
password
- Optional
dst
(destination after login)
On successful RADIUS authentication, the user is redirected to dst
.
๐ 5. Flow Summary: From Connect to Internet
๐ User Flow
- Connect to Hotspot SSID (managed by CAPsMAN)
- Gets IP in 10.0.30.0/24 (Hotspot pool)
- Browser auto-redirects to
hotspot.mikrotikmasters.com/login
- User sees styled login.html:
- Can log in OR click Sign up
- If signed up:
- Credentials stored in MySQL
- Auto-logged in to MikroTik via POST
- MikroTik sends credentials to RADIUS
- If accepted, user is granted internet access
- Redirected to their original URL (
dst
)
โ 6. Security & Recommendations
- Always use HTTPS for the splash page (setup Let’s Encrypt on web server)
- Sanitize and hash passwords if you use them beyond FreeRADIUS
- Consider Google or Facebook OAuth (when ready) by integrating with signup/login flow
- Protect your database and restrict access from specific MikroTik IPs
โ 7. Next Steps (Optional Enhancements)
- Add session tracking (PHP/MySQL)
- Add custom usage limits and expiry logic
- Integrate social login (Google/Facebook)
- Add logo and T&Cs links dynamically