You can connect to terminal using Winbox or using ssh (look below for how to connect to mikrotik)

Terminal and Scripts

Item names can be used instead of item number and are more stable since some other user can configure router in the same time or change scripts.

General menu commands:

  • print is to list all items, print file=my_file print to file, print detail print in format prop=value (oposite to print brief), print follow like tail -f, print from=my_property_name print single item find by name (similar to print where name=my_property_name), /ip route print where interface="ether1" filtering used for print, print dynamic print dynamic items. print static interval=1 print static items and refresh every 2 seconds
  • add to create new record. You can use copy-from. example is add address = / 24 network = broadcast = interface = Local
  • remove <id> remove record
  • disable <id> and enable <id>
  • move
  • edit <id> prop any property/param in editor edit pptp-out1 password
  • set <id> prop=value similar to edit, but for more properties. Use ! to unset :set 3 !password. To disable you can use set 3 disable=yes.
  • get <id> prop used for inline expressions :put [get 1 address]
  • find filtering :put [/interface find name~"ether"] this will return array of <id>. It can be used for example to remove all globals you can /system script environment remove [find], update specific item: /system logging set [find topics="warning"] action=disk
  • /export file=myfilename will export all configurations. You can export only not default with /export compact, or /export compact file=my_export to save to a file /dule.rsc which you can download using ftp. If you are not in root, for example you are in /ip it will export only changes to ip. You can /import myfilename.rsc to load configurations ie to run script. Add verbose=yes to see more info. If file name ends with auto than it will be executed when uploaded using FTP. In rsc file you can indent all commands with :%s/^\([as]\)/ \1/

Default configuration script (when you reset with power off and power on while holding reset button)

#| RouterMode:
#|  * WAN port is protected by firewall and enabled DHCP client
#|  * Ethernet interfaces (except WAN port ether1) are part of LAN bridge
#| LAN Configuration:
#|     IP address is set on bridge (LAN port)
#|     DHCP Server: enabled;
#| WAN (gateway) Configuration:
#|     gateway:  ether1 ;
#|     ip4 firewall:  enabled;
#|     NAT:   enabled;
#|     DHCP Client: enabled;
#|     DNS: enabled;

:log info Starting_defconf_script_;
# Apply configuration.
# these commands are executed after installation or configuration reset
:if ($action = "apply") do={
# wait for interfaces
:local count 0; 
:while ([/interface ethernet find] = "") do={ 
:if ($count = 30) do={
:log warning "DefConf: Unable to find ethernet interfaces";
:delay 1s; :set count ($count +1); 

 /interface list add name=WAN comment="defconf"
 /interface list add name=LAN comment="defconf"
 /interface bridge
   add name=bridge disabled=no auto-mac=yes protocol-mode=rstp comment=defconf;
 :local bMACIsSet 0;
 :foreach k in=[/interface find where !(slave=yes  || name~"ether1" || name~"bridge")] do={
   :local tmpPortName [/interface get $k name];
   :log info "port: $tmpPortName"
   :if ($bMACIsSet = 0) do={
     :if ([/interface get $k type] = "ether") do={
       /interface bridge set "bridge" auto-mac=no admin-mac=[/interface ethernet get $tmpPortName mac-address];
       :set bMACIsSet 1;
   /interface bridge port
     add bridge=bridge interface=$tmpPortName comment=defconf;
   /ip pool add name="default-dhcp" ranges=;
   /ip dhcp-server
     add name=defconf address-pool="default-dhcp" interface=bridge lease-time=10m disabled=no;
   /ip dhcp-server network
     add address= gateway= comment="defconf";
  /ip address add address= interface=bridge comment="defconf";
   /ip dhcp-client add interface=ether1 disabled=no comment="defconf";
 /interface list member add list=LAN interface=bridge comment="defconf"
 /interface list member add list=WAN interface=ether1 comment="defconf"
 /ip firewall nat add chain=srcnat out-interface-list=WAN ipsec-policy=out,none action=masquerade comment="defconf: masquerade"
 /ip firewall {
   filter add chain=input action=accept connection-state=established,related,untracked comment="defconf: accept established,related,untracked"
   filter add chain=input action=drop connection-state=invalid comment="defconf: drop invalid"
   filter add chain=input action=accept protocol=icmp comment="defconf: accept ICMP"
   filter add chain=input action=drop in-interface-list=!LAN comment="defconf: drop all not coming from LAN"
   filter add chain=forward action=accept ipsec-policy=in,ipsec comment="defconf: accept in ipsec policy"
   filter add chain=forward action=accept ipsec-policy=out,ipsec comment="defconf: accept out ipsec policy"
   filter add chain=forward action=fasttrack-connection connection-state=established,related comment="defconf: fasttrack"
   filter add chain=forward action=accept connection-state=established,related,untracked comment="defconf: accept established,related, untracked"
   filter add chain=forward action=drop connection-state=invalid comment="defconf: drop invalid"
   filter add chain=forward action=drop connection-state=new connection-nat-state=!dstnat in-interface-list=WAN comment="defconf:  drop all from WAN not DSTNATed"
   /ip neighbor discovery-settings set discover-interface-list=LAN
   /tool mac-server set allowed-interface-list=LAN
   /tool mac-server mac-winbox set allowed-interface-list=LAN
 /ip dns {
     set allow-remote-requests=yes
     static add name=router.lan address=

# Revert configuration.
# these commands are executed if user requests to remove default configuration
:if ($action = "revert") do={
/user set admin password=""
 /system routerboard mode-button set enabled=no
 /system routerboard mode-button set on-event=""
 /system script remove [find comment~"defconf"]
 /ip firewall filter remove [find comment~"defconf"]
 /ip firewall nat remove [find comment~"defconf"]
 /interface list member remove [find comment~"defconf"]
 /interface detect-internet set detect-interface-list=none
 /interface detect-internet set lan-interface-list=none
 /interface detect-internet set wan-interface-list=none
 /interface detect-internet set internet-interface-list=none
 /interface list remove [find comment~"defconf"]
 /tool mac-server set allowed-interface-list=all
 /tool mac-server mac-winbox set allowed-interface-list=all
 /ip neighbor discovery-settings set discover-interface-list=!dynamic
   :local o [/ip dhcp-server network find comment="defconf"]
   :if ([:len $o] != 0) do={ /ip dhcp-server network remove $o }
   :local o [/ip dhcp-server find name="defconf" !disabled]
   :if ([:len $o] != 0) do={ /ip dhcp-server remove $o }
   /ip pool {
     :local o [find name="default-dhcp" ranges=]
     :if ([:len $o] != 0) do={ remove $o }
   :local o [/ip dhcp-client find comment="defconf"]
   :if ([:len $o] != 0) do={ /ip dhcp-client remove $o }
 /ip dns {
   set allow-remote-requests=no
   :local o [static find name=router.lan address=]
   :if ([:len $o] != 0) do={ static remove $o }
 /ip address {
   :local o [find comment="defconf"]
   :if ([:len $o] != 0) do={ remove $o }
 :foreach iface in=[/interface ethernet find] do={
   /interface ethernet set $iface name=[get $iface default-name]
 /interface bridge port remove [find comment="defconf"]
 /interface bridge remove [find comment="defconf"]
:log info Defconf_script_finished;

this is log

00:00:16 system,error,critical router rebooted without proper shutdown, probably power outage 
00:00:16 script,info Starting_defconf_script_ 
00:00:18 system,info interface list added 
00:00:18 system,info interface list added 
00:00:18 system,info device added 
00:00:18 script,info port: ether2 
00:00:18 system,info device changed 
00:00:18 system,info bridge port added 
00:00:18 script,info port: ether3 
00:00:18 system,info bridge port added 
00:00:18 script,info port: ether4 
00:00:18 system,info bridge port added 
00:00:18 script,info port: ether5 
00:00:18 system,info bridge port added 
00:00:18 system,info pool default-dhcp added 
00:00:18 system,info dhcp server defconf added 
00:00:18 system,info dhcp network added 
00:00:18 system,info address added 
00:00:19 system,info dhcp client added 
00:00:19 system,info interface list member added 
00:00:19 system,info interface list member added 
00:00:19 system,info nat rule added 
00:00:20 system,info filter rule added 
00:00:20 system,info filter rule added 
00:00:20 system,info filter rule added 
00:00:20 system,info filter rule added 
00:00:20 system,info filter rule added 
00:00:20 system,info filter rule added 
00:00:20 system,info filter rule added 
00:00:20 system,info filter rule added 
00:00:20 system,info filter rule added 
00:00:20 system,info filter rule added 
00:00:20 system,info config changed 
00:00:20 system,info mac-server interface changed 
00:00:20 system,info mac winbox setting changed 
00:00:21 system,info dns changed 
00:00:21 system,info static dns entry added 
00:00:21 script,info Defconf_script_finished 
00:00:39 interface,info ether2 link up (speed 100M, full duplex) 

Keyboard shortcuts similar to linux: <c-k> clear to end of line, <c-b> <c-f> back and forward one char, <c-a> <c-e> jump to begging or end of line.

Safe mode starts with <c-x>, and ends and keep all changes also with <c-x>. twice <c-x> will empty safe mode action list so you can store more. <c-d> undo all safe mode changes, while /quit does not. You can review all safe mode changes (while you are in safe mode) with /system history print If you want to paste some commands, but autocompletion triggers some errors, you can go to Safe mode so autocompletion is triggered only on tab. Note that all commands that work with data, starts with :

  • # comment should start on beggining of the line
  • / move top level, .. move up
  • ? show available commands
  • :delay 3 do nothing for 3 seconds. Could be miliseconds :delay 10ms

In terminal you can set variables. Local variables only works inside { }, They are ignored if used at root of console. Always use snakeCase, so you do not overlap with keywords. If you need to use underscore than wrap variable with quotes. You can pring value with :put

:global globalVar;
  :local localVar localValue;
  :set localVar 1;
  :put $localVar;
:global "var_with_underscore";
:global "var-with-dash";

In scripts when hotspot user logs in you can access $user variable (it is username)

:global username [/ip hotspot active find user=$user];
:global userip [/ip hotspot active get $username address];
:log info $userip

# or in one line
/ip hotspot user profile set [ find default=yes ] on-login="if ([/ip hotspot active get [find user=\$user] address] in do={:log warning \"THIS SUBSCRIBER HAS EXPIRED! ASSIGNED IP IN\"}"


:if () do={

# check if some record exists
:if ([:len [/system package find name="dhcp" !disabled]] != 0) do={
# check if ping did not timeout
:if ([:ping count=5] = 0) do={
  :error "Ping timeout 5 times"


:put message="some message"
# join long lines with \
# white space is not allowed around "="

Data types

  • num
  • bool
  • str string can be concatenated with "asd"."qwe". Lenght of string with :put [:len "asdf"]. Interpolate with :put "a=$($a))" or :put "we have find $[ :len [/ip route find] ] routes"
  • ip
  • ip-prefix
  • id prefixed with *
  • time
  • array can be concatenated with comma :put ({1;2;3}, 5). Can also use named elements {1; a=2; "b3"=3; 4} and than can be accessed with hash arrow but it needs to be grouped with brackets :set ($a->"a") 22. Positional elements can be accessed with :put [:pick $a 0] Arrays can be generated from string with :local myArray [:toarray $myStr]
  • nil

You can convert data

#convert string to array
:local myStr "1,2,3,4,5";
:put [:typeof $myStr];
:local myArr [:toarray $myStr];
:put [:typeof $myArr]


  • in logical belongs :put ( in;
  • [] square brackets is command substitution :put [ :len "my string" ];. Only one line is allowed (commands can be separated with ;). To use in menu commands wrap with a string add address="$[ $ip . "1" ]". It is not allowed to have nested substitution.
  • () round brackets sub expression or grouping operator :put ( "value is " . (4+5));


:find there is also find command but it is only for filtering. :put [:find "asd" "s"]; return position of substring or array (in this example 1 so if you pick to that position s will not be included), can accept integer that means where to start (-1 is before start, 0 is to ignore first). If you want to know if it founds try this:

:if ([:len [:find "abcd" "x"]] > 0) do={:put "Found";} else={:put "Not Found";};


:pick $myVar <start> <end> return substring :put [:pick "abcde" 1 3] will return bc. Start can be -1 or 0 to pick from beggining.


:for i from=1 to=10 step=2 do= {} iterate for loop :do { } while=() or :while () do={ } while loop :foreach i in=$myArray do= { } iterate array elements


Functions can accept named $argName or unnamed arguments $1, $2 .... Avoid using parameters with same name as global variables.

:global myFunc do={
  :local sum ($a + $b)
  :return $sum

# call function with square brackets
:put [$myFunc a=1 b=2]

To call another function from inside function, you need to declare it

:global funcA do={ :return 5 }
:global funcB do={
  :global funcA;
  :return ([$funcA] + 4)

Parse can be used to define functions: :global myFunc [:parse ":put hello!"];

Logs and debug

You can print to terminal with :put message=hi or to log :log info hi (log can be used both as /menu and :command). :log warning "My message" write to topic: error, info and warning. You can define topis on /system logging and which action to perform. Actions could be memory (shows in /log print), echo (show in console), disk (write to file), email and remote (from webproxy to Kiwi server). Topics could be for example hotspot, pppoe. To set warnings to write to file (anyway it will show in /log just if we need to save for later use)

# set warning to disk
/system logging set [find topics="warning"] action=disk
:log warning "WAN started "

to see all logs on host you can run

ssh admin@$MIKROTIK_IP log print follow

:put [:time { :delay 100ms }] show time needed to execute command :environment print show all variables and functions. They are defined as records on /system script environment print :do {} on-error={ :put "my script failed" } catch run time errors :error "Message" stop executing the script

Run command in background and remove it

  :local pid [:execute {/interface print follow}]`
  :delay 5s
  :do { /system script job remove $pid } on-error={}

Scripts can be stored and triggered on event or by another script. For example I use my helper functions and load them from other scripts. To create use winbox (copy and paste) since $, " and \ must be escaped with preceding backslash \ if you want to create using terminal /system script add name=helpers source='/ip add...'. Before use you need to run them

/system script run helpers

To run script from flash you can use import

import flash/my.rsc

To replace mask with 1 you can

# helpers.script
# Add to /system scripts using Windbox
# if using terminal you need to escape $ " \
# before use you need to call
# /system script run helpers

# Strip Mark from IP
# :put [$stripMask]
# :put [$stripMask]
:global stripMask do={
  :local slashPosition [:find $1 "/"]
  if ([:len $slashPosition] > 0) do={
    :return [:pick $1 -1 $slashPosition ]
  } else= {
    :return $1

# Replace Last Number in IP address
# :put [$replaceLastNumber 1]
# :put [$replaceLastNumber "2"]
:global replaceLastNumber do={
  :local firstDot [:find $1 "." -1]
  :local secondDot [:find $1 "." $firstDot]
  :local thirdDot [:find $1 "." $secondDot]
  :return ([:pick $1 -1 $thirdDot] . "." . $2)

Script to send system log to email


To connect using IP you need to create IP -> Addresses -> New Address. Also you need to setup routes with gateway that are provided by isp (or it is your adsl router ip address). Route with dst-address= applies to every destination address.

Walled garden /ip hotspot walled-garden can be used to allow deny specific host in http and https requests.

/ip hotspot walled-garden
add dst-host=:^ path=":/test\$"

/ip hotspot walled-garden ip can be used to allow deny specific host and IP requests

/ip hotspot walled-garden ip
add action=accept disabled=no

# allow google dns and specific site
add action=accept disabled=no dst-address=
add action=accept disabled=no dst-address=IpOfMySERVER

dst-address can be a Destination IP address list so you can group them… HTTPS can not be redirected since it starts in the browser and it creates a socket destionation_ip:443/TCP and mikrotik intercepts that and can not provide valid certificate for this url, so hotspot can’t redirect https to login page. For non HSTS site Mikrotik can use self signed certificate for that domain and redirect https requests. You can have two address pools (one for authenticated and one for non auth users) and ARP feature could be set to reply-only to prevent network access using static IP addresses.

Connect to Mikrotik

You can use web interface You can FTP to Mikrotik ftp (username admin and blank password).

ssh-keygen -t dsa
# username: admin
# password: blank
> ls
> mkdir backups
> cd backups
> cd ..
> put local_file_name
> get remote_file_name
> exit

Or in one line you can use lftp

lftp -u admin, -e "cd hotspot;put doc/mikrotik/login.html;exit"

Also you can SSH

If you get warnings

Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that a host key has just been changed.
The fingerprint for the DSA key sent by the remote host is
Please contact your system administrator.
Add correct host key in /home/orlovic/.ssh/known_hosts to get rid of this message.
Offending DSA key in /home/orlovic/.ssh/known_hosts:146
  remove with:
  ssh-keygen -f "/home/orlovic/.ssh/known_hosts" -R ""
DSA host key for has changed and you have requested strict checking.
Host key verification failed.

you can disable them in command

ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no admin@$MIKROTIK_IP

or in settings

# ~/.ssh/config
  UserKnownHostsFile /dev/null
  StrictHostKeyChecking no

Reset router with ssh

/system reset-configuration no-defaults=yes keep-users=yes run-after-reset=flash/wan.rsc

One short beep is that reseting started, normal beep is that actual reset occured, and two beeps are ready.

Since there could be some initialization problems, you should add delay in rsc files. To iterate command until it success you can try this script

# this succeed since we do not use network
/ip pool
add name=dhcp-pool-my-comp ranges=

:local repeat true
:local counter 0
:while ($repeat) do={
  :do {
    # this raise error on boot, so repeat untill succeed
    /ip dhcp-server
    add address-pool=dhcp-pool-my-comp disabled=no interface=ether2 name=\
    :set repeat false
  } on-error= {
    :delay 1
    :set counter ($counter + 1);
    :log warning "delay $counter"
    :if ($counter=50) do={ :set repeat false }


  @connection =
    host: @nas.nasname,
    user: @nas.username,
    pass: @nas.password,
    port: @nas.api_port,
    unencrypted_plaintext: true,
  result = @connection.get_reply(

My hotspot testing

I have two ethernet cards: eth0 (Atheros, bottom port) and eth1 (Intel, upper port). I connected my eth0 to ether2 and later eth1 to ether3. First port ether1 on Mikrotik is used like WAN and connected to my ADSL router Second mikrotik port ether2 is my computer connection.

/ip address
# my wan is
add address= interface=ether1
# my comp is on 192.168.5.*
add address= interface=ether2

/ip pool
add name=dhcp-pool5 ranges=
/ip dhcp-server
add address-pool=dhcp-pool5 disabled=no interface=ether2 name=dhcp5
/ip dhcp-server network
add address= dns-server= gateway=

/ip dns
set allow-remote-requests=yes servers=
/ip route
add distance=1 gateway=

# IMPORTANT to masquerade if you are behind other routers
/ip firewall nat
add action=masquerade chain=srcnat out-interface=ether1

Third mikrotik port ether3 is connected to my eth1 ethernet and bridged to Virtual Box. Mikrotik is using Radius server PUBLIC_RADIUS_IP but inside VPN with VPN_USERNAME and VPN_PASSWORD (so use PRIVATE_RADIUS_IP) on the mikrotik hosted in the cloud MIKROTIK_IN_THE_CLOUD_IP. There could be also PUBLIC_RADIUS_BACKUP_IP, and PRIVATE_RADIUS_BACKUP_IP. RADIUS_SECRET is needed for communication.

:global PUBLIC_RADIUS_IP ....
:global PRIVATE_RADIUS_IP ....
:global VPN_USERNAME ....
:global VPN_PASSWORD ....

# Interfaces -> + -> PPTP client -> Dial Out
# VPN_PASSWORD, uncheck pap and chap, check mschap1 mschap2
/interface pptp-client
add allow=mschap1,mschap2 connect-to=$MIKROTIK_IN_THE_CLOUD_IP disabled=no \
  name=pptp-out1  password=$VPN_PASSWORD user=$VPN_USERNAME

# since there could be only one pptp client connection for specific username,
# check if connection established correctly

# IP -> Routes -> +
# Dst. Address: PRIVATE_RADIUS_IP_0/24 Gateway: pptp-out1
# Dst. Address: PRIVATE_RADIUS_BACKUP_IP_0/24 Gateway: pptp-out1
/ip route
add distance=1 dst-address=PRIVATE_RADIUS_IP_0/24 gateway=pptp-out1
add distance=1 dst-address=PRIVATE_RADIUS_BACKUP_IP_0/24 gateway=pptp-out1
# maybe also vpn private network

# IP -> Firewall -> NAT -> +
# Chain: srcnat, tab Action select masquerade
/ip firewall nat
add action=masquerade chain=srcnat out-interface=pptp-out1

If you want to ping from comp to hotspot user, user need to be logged in. If it is logged in user can ping router ping, and other hotspot users ping and host comp ping Also from host you can ping user. If it not logged in, user can not ping router and host can not ping user.

If /login shows “Error 404: Not Found” that means you are connected with ether2 and not ehter3 (hotspot)

If not logged in it can be byepassed with /ip hotspot ip-binding add address= type=bypassed, but than it can not access login or status pages Or you can add Walled garden /ip hotspot walled-garden ip add dst-address= action=accept and than it will be accessible in both directions, from and to

For Hotspot we allow ALLOW_SITE_IP and Google DNS If inside hotspot client does not work, it is because of DNS is available but redirects to https, which is than redirected to login page, but since we do not have proper certification, there is an error. Try to use some of google IP address to see if it will redirect to hotspot login page, or use

:global ALLOW_SITE_IP = ....

# IP -> Hotspot -> Hotspot setup
# HotSpot interface = ether3, Local Address of Network =
# check Masquerade Network, Address Pool of Network =,
# Select Certificate = none, IP Address of SMTP server =
# DNS Server =, DNS Name of local hotspot server = (empty)
# Hotspot user Name: admin, password: (empty)
/ip hotspot profile
add hotspot-address= login-by=cookie,http-pap name=hsprof1 \
    nas-port-type=ethernet use-radius=yes
/ip pool
add name=hs-pool-3 ranges=
/ip dhcp-server
add address-pool=hs-pool-3 disabled=no interface=ether3 lease-time=1h name=dhcp1
/ip hotspot
add address-pool=hs-pool-3 disabled=no interface=ether3 name=hotspot1 profile=hsprof1
/ip address
add address= comment="hotspot network" interface=ether3
/ip dhcp-server network
add address= comment="hotspot network" gateway=
/ip firewall filter
add action=passthrough chain=unused-hs-chain comment="place hotspot rules here" disabled=yes
/ip firewall nat
add action=passthrough chain=unused-hs-chain comment="place hotspot rules here" disabled=yes
add action=masquerade chain=srcnat comment="masquerade hotspot network" src-address=
/ip hotspot user
add name=admin

# IP -> Hotspot -> Server Profiles -> hsprof
# -> Login : check HTTP PAP, uncheck HTTP CHAP
# -> RADIUS : check Use RADIUS, NAS Port Type: ethernet
# this properties: login-by, nas-port-type and use-radius already include above

# IP -> Hotspot -> Walled Garden IP List -> +
# Dst. Address =
# IP -> Hotspot -> Walled Garden IP List -> +
# Dst. Address = (or ip address for older RouterOS)
/ip hotspot walled-garden
add comment="place hotspot rules here" disabled=yes
/ip hotspot walled-garden ip
add action=accept disabled=no dst-address=
add action=accept disabled=no dst-address=$ALLOW_SITE_IP

# Radius -> +
# check ppp, check hotspot, Address = PRIVATE_RADIUS_IP, Secret = RADIUS_SECRET,
# Timeout = 5000ms
add address=$PRIVATE_RADIUS_IP secret=secret service=ppp,hotspot timeout=5s
add address=$PRIVATE_RADIUS_BACKUP_IP secret=secret service=ppp,hotspot timeout=5s

Hotspot templates Html pages

There are several pages that are rendered on mikrotik:

  • redirect.html immediatelly redirect with http status (also with meta but I think that is not used). When I remove first two lines that perform http-status and http-header

    $(if http-status == 302)Hotspot redirect$(endif)
    $(if http-header == "Location")$(link-redirect)$(endif)

    than it will use some other default template which redirects. If I change $(link-redirect) to google, it will redirect to google. So you can not override this file to not perform redirect.

GET /login

  • if user is not authenticated than renders login.html to ask user for username and password. Parameters on this url can be:
    • username, password (plain for PAP or md5 hash of chap-id, password and challenge for CHAP)
    • dst original requested url before redirect
    • popup show status window on success login
    • radius some radius params POST & GET /login
  • if user is successfully authenticated or already authenticated than it renders alogin.html page. It redirects in javascript or meta to link-redirect (it is external page or
  • if not successfull auth (wrong password or username) it renders flogin.html if exists or redirect with http status 302 to login

GET /status

  • if auth user than render status.html show status with logout form that submit to /logout Form can have erase-cookie to erase cookie so user need to insert username/password again even cookie login is enabled
  • if not auth user and if fstatus.html exists than use that template, otherwise use redirect.html to redirect to /login

GET /logout

  • if auth user it will log out and render logout.html and provide a link to /login (refresh will not work since user is deauthenticated)
  • if not auth user than redirect 302 to /login or use flogout.html if exists

  • error.html show fatal errors

GET external page

  • if user is not auth it renders rlogin.html if exists or redirect 302 to /login?dst=target_page. It can be used for external login

GET “/”on hotspot

  • rstatus.html root on hotspot host for auth user,
  • radvert.html when advertisement is due to be displayed,

You can have different pages for different /ip hotspot profile print where html-directory=hotspot. You can create subfolder for translation (lv) and use parametar target=lv. Other links will stay in that subfolder.

Links variables which can be used like $(varName):

  • link-login link to login page including original URL requested
  • link-login-only link to login page, not including original URL requested
  • link-orig original url requested
  • link-logout link to logout
  • link-status link to status

General variables:

  • logged-in “yes” is user is logged in, otherwise “no”
  • mac MAC of the user
  • username name of the user
  • error error message
  • hostname DNS or IP address of hostspot servlet
  • server-address hostspot server address with port
  • ssl-login “yes” if https methods was used to access this page
  • interface-name bridge or interface name
  • ip ip address of the client
  • host-ip client ip address from /ip hotspot host

Inside templates $(if varName) ... $(elif varName) ... $(else) ... $(endif) or $(if logged-in = 'yes') can be used for conditionals.

You can POST to /login.html with username and password, and user will be authenticated and redirected. You can also GET and user will be authenticated but /status.html will be shown. It’s better to use POST with https.

If hotspot user after logout, goes to status page, if will be logged in again, because of cookie. You need to erase cookie if you want user to type password again.

<input type="hidden" name="erase-cookie" value="on">

or you can disable cookie login for hotspot profile /ip hotspot profile set default login-by=http-chap

External login

Note that form url should point to /login path


Firewall obsolete

chain is name of rule group. Rulles are taken from the chain in the order they are listed and if packet matches the criteria than action is perfomed and no more rules are processed in that chain (except for passthrough action), if a packet has not matched any rule whith the chain then it is accepted.

  • input for connections to the router (destination is one of the router’s addresses)
  • forward for connection going through router
  • output for connections from the router (originated the the router)
  • my_name for example hotspot for hotspot connections

action could be:

  • accept to allow packet to pass through and no more rules are applied
  • redirect replaces destionation port to to-ports=64872 and destination address to-addresses to one of the routers local address.
  • jump with jump-target=chain-name jumps to the chain which can return usually with defined hotspot for example: hotspot=from-client,!auth, to-client,auth, local-dst
  • return jump back to the chain where the jump took place.
  • drop drop this packet without sending ICMP reject message
  • reject with reject-with=tcp-reset or reject-with=icmp-net-prohibited
  • passthrough continue rules on this chain (ignore current rule)
  • log add message to system log, same as when you check “Log” for other actions
  • add-dst-to-address-list, add-src-to-address-list add address to my-list address-list=my-list
  • src-nat replaces src-address with to-addresses and to-ports
  • dst-nat replaces dst-address with to-addresses and to-ports

dst-address ip address range ip/mask or ip-ip. dst-port destination port dst-address-list list protocol: tcp

hotspot multiple choice: auth,from client,to client,http,local dst. There are dynamic rules that jump to another chain:

  • hs-unauth for chain: forward and hotspot: from client,!auth
  • hs-unauth-to for chain: forward and hotspot: !auth,to client
  • hs-input for chain: input and hotspot: from client. which jumps to pre-hs-input and at the end there is reject for hs-unauth, hs-unauth-to. If you want to allow some trafic to (and from) add action return so it does not reach those reject rules. You need to add twice for both incoming and outgoing packets.

You need to check both tables: Firewall Rules and NAT. For Hotspot in NAT firewall table there are redirect to port 64872-64875.

  • 64872 port provides DNS service for Hotspot users 5 chain=hotspot action=redirect to-ports=64872 dst-port=53 protocol=tcp
  • 64873 port is hotspot http servlet port (when user access router IP) 6 chain=hotspot action=redirect to-ports=64873 hotspot=local-dst dst-port=80 protocol=tcp
  • 64874 port is Walled Garden proxy server 12 D chain=hs-unauth action=redirect to-ports=64874 dst-port=80 protocol=tcp
  • 64875 port is hotspot https servlet port 8 D chain=hotspot action=redirect to-ports=64875 hotspot=local-dst dst-port=443 protocol=tcp

Redirection in NAT should be done in dstnat chain with dst-nat action, which can accept address-to=

IP Proxy

Proxy can be used for increasing web speed or for firewall, when we redirect users to the proxy and show them local page. For example when Radius assign user some ip UNAUTHORIZED_IP we can redirect all trafic from that UNAUTHORIZED_IP to our hotspot page unauthorized.html

# assign address to interface
/ip address
add address=UNAUTHORIZED_IP_with_0_1/16 interface=ether3

# redirect all requests to 8080
/ip firewall nat
add action=redirect chain=dstnat protocol=tcp \
src-address=UNAUTHORIZED_IP_with_0_1/16 to-ports=8080

# enable ip proxy
/ip proxy
set enabled=yes

# redirect path
/ip proxy access
print detail
# you can redirect specific sites like facebook
add action=deny redirect-to=
# you can allow specific sites
add action=allow dst-host=*

HTTPS and redirect Https use secure connection (SSL/TLS handshake) with a server so request to which is redirected at firewall level will not work with your server/proxy/router. In corporate networks user can add Exception to install your certificate (that covers major domains like and it can redirect (maybe it is using transparent web proxy) Modern browsers (firefox 58) will show login button (link to for hsts and non hsts site, so users can continue with login.

Log in to network

You must log in to this network before you can access the Internet.

This site uses HTTP Strict Transport Security (HSTS) to specify that Firefox may only connect to it securely. As a result, it is not possible to add an exception for this certificate.

https redirect non hsts site https redirect hsts site

Also modern Android OS will automatically open login page when it is connected to wifi (probably it checks if redirection occurs to some well known site) and if you do not connect, it will disconnect from wifi network.

Lets encrypt can be used to generate certs.

# get acme
curl | sh
# issue a cert --issue --dns -d
# check if record exists
host -t txt
# renew when txt record is available --renew -d

NAT Nat is used to rewrite source (destination) ip address for source (and destination) NATted network. masquerade is a form of source NAT where to-addresses is not needed to be specified, outgoing interface is used. redirect is a form of destination NAT where to-addresses is not used, incoming interface is used.

For easier manipulation you can create lists

/ip firewall address-list
# at this moment, mikrotik will fetch ip from dns and save to address list

Mangle mark routing

Mikrotik youtube tutorials

Ruby Gem


  • resolve hostname and update record

  • user scripts
  • set ports to work as switch is to create a bridge and add port to it for each interface
    /interface bridge
    add name=bridge
    /interface bridge port
    add bridge=bridge interface=ether1
    add bridge=bridge interface=ether2
    add bridge=bridge interface=ether3
    add bridge=bridge interface=ether4
    add bridge=bridge interface=ether5


We have old router