diff --git a/web/alias-server/src/chronik.js b/web/alias-server/src/chronik.js
--- a/web/alias-server/src/chronik.js
+++ b/web/alias-server/src/chronik.js
@@ -6,6 +6,26 @@
 
 module.exports = {
     chronik,
+    getChaintip: async function () {
+        let info;
+        try {
+            info = await chronik.blockchainInfo();
+            return info;
+        } catch (err) {
+            log(`Error in getChainTip()`, err);
+            return false;
+        }
+    },
+    getBlockDetails: async function (blockhash) {
+        let blockdetails;
+        try {
+            blockdetails = await chronik.block(blockhash);
+            return blockdetails;
+        } catch (err) {
+            log(`Error in getBlockDetails()`, err);
+            return false;
+        }
+    },
     getTxHistoryPage: async function (hash160, page = 0) {
         let txHistoryPage;
         try {
diff --git a/web/alias-server/src/websocket.js b/web/alias-server/src/websocket.js
--- a/web/alias-server/src/websocket.js
+++ b/web/alias-server/src/websocket.js
@@ -10,7 +10,8 @@
     removeUnconfirmedTxsFromTxHistory,
 } = require('./utils');
 const { returnTelegramBotSendMessagePromise } = require('./telegram');
-const { chronik } = require('./chronik');
+const { chronik, getChaintip, getBlockDetails } = require('./chronik');
+const { isFinalBlock } = require('./rpc');
 const axios = require('axios');
 
 module.exports = {
@@ -34,16 +35,117 @@
         // Determine type of tx
         const { type } = wsMsg;
         log(`msg type: ${type}`);
-        let isAppStartup = type === 'startup';
         // type can be AddedToMempool, BlockConnected, or Confirmed
         // For now, we are only interested in "Confirmed", as only these are valid
         // We will want to look at AddedToMempool to process pending alias registrations later
+        let tipHash, tipHeight, chaintipInfo;
         switch (type) {
             case 'startup':
+                log(`Checking for new aliases on startup`);
+
+                // If this is app startup, get the latest tipHash and tipHeight by querying the blockchain
+                chaintipInfo = await getChaintip();
+
+                if (!chaintipInfo) {
+                    // If you have an API call error on app startup, exit the app
+                    // Chronik must be online to add new valid aliases
+                    log(`Exiting app startup due to error in getChaintip()`);
+                    return process.exit(1);
+                }
+
+                tipHash = chaintipInfo.tipHash;
+                tipHeight = chaintipInfo.tipHeight;
+
+            // Fallthrough
             case 'BlockConnected': {
-                isAppStartup
-                    ? log(`Checking for new aliases on startup`)
-                    : log(`New block found: ${wsMsg.blockHash}`);
+                /*
+                 * BlockConnected callback
+                 *
+                 * This is where alias-server queries the blockchain for new transactions and
+                 * parses those transactions to determine if any are valid alias registrations
+                 *
+                 * The database may only be updated if we have a known blockhash and height with
+                 * isFinalBlock = true confirmed by avalanche
+                 *
+                 * A number of error conditions may cause the loop to exit before any update to
+                 * the database occurs.
+                 *
+                 * If alias-server determines a blockhash and height with isFinalBlock === true,
+                 * valid alias registrations will be processed up to and including that blockheight
+                 *
+                 * Otherwise the loop will exit before any updates are made to the database
+                 *
+                 * Note: websockets disconnect and reconnect frequently. It cannot be assumed that
+                 * every found block will triggger this loop. So, the loop must be designed such that
+                 * it will always update for all unseen valid alias registrations.
+                 *
+                 */
+
+                // Use the block just found by chronik as your most recent block
+                if (typeof tipHash === 'undefined') {
+                    tipHash = wsMsg.blockHash;
+                    log(`New block found: ${tipHash}`);
+                }
+
+                // Get tip height
+                const blockdetails = await getBlockDetails(tipHash);
+                if (!blockdetails) {
+                    // If you have an API call error, do not complete the loop
+                    // Chronik must be online to add new valid aliases
+                    log(
+                        `Exiting loop triggered by block ${tipHash} due to error in getBlockDetails()`,
+                    );
+                    // TODO admin notification in diff enabling this feature
+                    return;
+                }
+
+                // chronik blockdetails returns the block height at the 'blockInfo.height' key
+                tipHeight = blockdetails.blockInfo.height;
+                // If you don't get what you expect for tipHeight, exit the loop
+                // Should be a number e.g. 785748
+                if (typeof tipHeight !== 'number') {
+                    return;
+                }
+
+                // Initialize isAvalancheFinalized as false. Only set to true if you
+                // prove it so with a node rpc call
+                let isAvalancheFinalized = false;
+
+                // Wait 10s before checking this block for avalanche finality
+                await new Promise(resolve => setTimeout(resolve, 10000));
+
+                // Check to see if block tipHash has been finalized by avalanche
+                try {
+                    isAvalancheFinalized = await isFinalBlock(tipHash);
+                } catch (err) {
+                    log(
+                        `Error checking avalanche final status of block ${tipHash}`,
+                        err,
+                    );
+                }
+
+                // Exit this loop if your block is not finalized by avalanche
+                if (!isAvalancheFinalized) {
+                    /* If this is normal operation, i.e. loop triggered by chronik websocket
+                     * msg confirming a new block has been found, exit loop.
+                     * You might not update valid aliases until the next block
+                     * This is an acceptable condition, and already must be designed for as
+                     * websocket disconnects mean the app does not receive a msg for every found block
+                     */
+                    log(
+                        `Block ${tipHash} is not avalanche finalized. Exiting loop before processing any alias txs.`,
+                    );
+                    return;
+                }
+
+                // If you are here, you have a finalized block of known height and hash
+                log(
+                    `Chaintip ${tipHeight}:${tipHash} is finalized by avalanche.`,
+                );
+
+                log(
+                    `Transactions with blockheight ${tipHeight} or lower may be valid alias registrations.`,
+                );
 
                 // Get the valid aliases already in the db
                 let validAliasesInDb;