diff --git a/add-on/src/lib/ipfs-client/index.js b/add-on/src/lib/ipfs-client/index.js
index c7b69ecae..a18ade5e4 100644
--- a/add-on/src/lib/ipfs-client/index.js
+++ b/add-on/src/lib/ipfs-client/index.js
@@ -6,11 +6,9 @@ const debug = require('debug')
 const log = debug('ipfs-companion:client')
 log.error = debug('ipfs-companion:client:error')
 
-const browser = require('webextension-polyfill')
 const external = require('./external')
 const embedded = require('./embedded')
 const embeddedWithChromeSockets = require('./embedded-chromesockets')
-const { webuiCid } = require('../state')
 
 let client
 
@@ -33,7 +31,7 @@ async function initIpfsClient (opts) {
 
   const instance = await client.init(opts)
   easeApiChanges(instance)
-  _reloadIpfsClientDependents(instance) // async (API is present)
+  preloadWebui(instance, opts)
   return instance
 }
 
@@ -43,43 +41,28 @@ async function destroyIpfsClient () {
       await client.destroy()
     } finally {
       client = null
-      await _reloadIpfsClientDependents() // sync (API stopped working)
     }
   }
 }
 
-function _isWebuiTab (url) {
-  const bundled = !url.startsWith('http') && url.includes('/webui/index.html#/')
-  const ipns = url.includes('/webui.ipfs.io/#/')
-  return bundled || ipns
-}
-
-async function _reloadIpfsClientDependents (instance, opts) {
-  // online || offline
-  if (browser.tabs && browser.tabs.query) {
-    const tabs = await browser.tabs.query({})
-    if (tabs) {
-      tabs.forEach((tab) => {
-        // detect bundled webui in any of open tabs
-        if (_isWebuiTab(tab.url)) {
-          browser.tabs.reload(tab.id)
-          log('reloading bundled webui')
-        }
-      })
-    }
-  }
-  // online only
-  if (client && instance) {
-    if (webuiCid && instance.refs) {
-      // Optimization: preload the root CID to speed up the first time
-      // Web UI is opened. If embedded js-ipfs is used it will trigger
-      // remote (always recursive) preload of entire DAG to one of preload nodes.
-      // This way when embedded node wants to load resource related to webui
-      // it will get it fast from preload nodes.
-      log(`preloading webui root at ${webuiCid}`)
-      instance.refs(webuiCid, { recursive: false })
-    }
-  }
+function preloadWebui (instance, opts) {
+  // run only when client still exists and async fetch is possible
+  if (!(client && instance && opts.webuiRootUrl && typeof fetch === 'function')) return
+  // Optimization: preload the root CID to speed up the first time
+  // Web UI is opened. If embedded js-ipfs is used it will trigger
+  // remote (always recursive) preload of entire DAG to one of preload nodes.
+  // This way when embedded node wants to load resource related to webui
+  // it will get it fast from preload nodes.
+  const webuiUrl = opts.webuiRootUrl
+  log(`preloading webui root at ${webuiUrl}`)
+  return fetch(webuiUrl, { redirect: 'follow' })
+    .then(response => {
+      const webuiPath = new URL(response.url).pathname
+      log(`preloaded webui root at ${webuiPath}`)
+      // trigger recursive remote preload in js-ipfs
+      instance.refs(webuiPath, { recursive: false })
+    })
+    .catch(err => log.error(`failed to preload webui root`, err))
 }
 
 const movedFilesApis = ['add', 'addPullStream', 'addReadableStream', 'cat', 'catPullStream', 'catReadableStream', 'get', 'getPullStream', 'getReadableStream']
diff --git a/add-on/src/lib/ipfs-companion.js b/add-on/src/lib/ipfs-companion.js
index ffc6f455b..8f350e397 100644
--- a/add-on/src/lib/ipfs-companion.js
+++ b/add-on/src/lib/ipfs-companion.js
@@ -6,7 +6,6 @@ const log = debug('ipfs-companion:main')
 log.error = debug('ipfs-companion:main:error')
 
 const browser = require('webextension-polyfill')
-const toMultiaddr = require('uri-to-multiaddr')
 const { optionDefaults, storeMissingOptions, migrateOptions } = require('./options')
 const { initState, offlinePeerCount } = require('./state')
 const { createIpfsPathValidator } = require('./ipfs-path')
@@ -98,7 +97,8 @@ module.exports = async function init () {
   function registerListeners () {
     const onBeforeSendInfoSpec = ['blocking', 'requestHeaders']
     if (!runtime.isFirefox) {
-      // Chrome 72+  requires 'extraHeaders' for access to Referer header (used in cors whitelisting of webui)
+      // Chrome 72+  requires 'extraHeaders' for accessing all headers
+      // Note: we need this for code ensuring ipfs-http-client can talk to API without setting CORS
       onBeforeSendInfoSpec.push('extraHeaders')
     }
     browser.webRequest.onBeforeSendHeaders.addListener(onBeforeSendHeaders, { urls: ['<all_urls>'] }, onBeforeSendInfoSpec)
@@ -419,6 +419,7 @@ module.exports = async function init () {
       // See: https://github.com/ipfs/ipfs-companion/issues/286
       try {
         // pass the URL of user-preffered public gateway
+        // TOOD: plan to remove this
         await browser.tabs.executeScript(details.tabId, {
           code: `window.ipfsCompanionPubGwURL = '${state.pubGwURLString}'`,
           matchAboutBlank: false,
@@ -426,6 +427,7 @@ module.exports = async function init () {
           runAt: 'document_start'
         })
         // inject script that normalizes `href` and `src` containing unhandled protocols
+        // TOOD: add deprecation warning and plan to remove this
         await browser.tabs.executeScript(details.tabId, {
           file: '/dist/bundles/normalizeLinksContentScript.bundle.js',
           matchAboutBlank: false,
@@ -436,18 +438,6 @@ module.exports = async function init () {
         console.error(`Unable to normalize links at '${details.url}' due to`, error)
       }
     }
-    if (details.url.startsWith(state.webuiRootUrl)) {
-      // Ensure API backend points at one from IPFS Companion
-      const apiMultiaddr = toMultiaddr(state.apiURLString)
-      await browser.tabs.executeScript(details.tabId, {
-        runAt: 'document_start',
-        code: `if (!localStorage.getItem('ipfsApi')) {
-          console.log('[ipfs-companion] Setting API to ${apiMultiaddr}');
-          localStorage.setItem('ipfsApi', '${apiMultiaddr}');
-          window.location.reload();
-        }`
-      })
-    }
   }
 
   // API STATUS UPDATES
@@ -643,6 +633,7 @@ module.exports = async function init () {
         case 'ipfsApiUrl':
           state.apiURL = new URL(change.newValue)
           state.apiURLString = state.apiURL.toString()
+          state.webuiRootUrl = `${state.apiURLString}webui`
           shouldRestartIpfsClient = true
           break
         case 'ipfsApiPollMs':
@@ -651,7 +642,6 @@ module.exports = async function init () {
         case 'customGatewayUrl':
           state.gwURL = new URL(change.newValue)
           state.gwURLString = state.gwURL.toString()
-          state.webuiRootUrl = `${state.gwURLString}ipfs/${state.webuiCid}/`
           break
         case 'publicGatewayUrl':
           state.pubGwURL = new URL(change.newValue)
diff --git a/add-on/src/lib/ipfs-request.js b/add-on/src/lib/ipfs-request.js
index 5e5871b2a..16e471775 100644
--- a/add-on/src/lib/ipfs-request.js
+++ b/add-on/src/lib/ipfs-request.js
@@ -34,28 +34,6 @@ function createRequestModifier (getState, dnslinkResolver, ipfsPathValidator, ru
   const ignoredRequests = new LRU(requestCacheCfg)
   const ignore = (id) => ignoredRequests.set(id, true)
   const isIgnored = (id) => ignoredRequests.get(id) !== undefined
-
-  const acrhHeaders = new LRU(requestCacheCfg) // webui cors fix in Chrome
-  const originUrls = new LRU(requestCacheCfg) // request.originUrl workaround for Chrome
-  const originUrl = (request) => {
-    // Firefox and Chrome provide relevant value in different fields:
-    // (Firefox) request object includes full URL of origin document, return as-is
-    if (request.originUrl) return request.originUrl
-    // (Chrome) is lacking: `request.initiator` is just the origin (protocol+hostname+port)
-    // To reconstruct originUrl we read full URL from Referer header in onBeforeSendHeaders
-    // and cache it for short time
-    // TODO: when request.originUrl is available in Chrome the `originUrls` cache can be removed
-    const cachedUrl = originUrls.get(request.requestId)
-    if (cachedUrl) return cachedUrl
-    if (request.requestHeaders) {
-      const referer = request.requestHeaders.find(h => h.name === 'Referer')
-      if (referer) {
-        originUrls.set(request.requestId, referer.value)
-        return referer.value
-      }
-    }
-  }
-
   const preNormalizationSkip = (state, request) => {
     // skip requests to the custom gateway or API (otherwise we have too much recursion)
     if (request.url.startsWith(state.gwURLString) || request.url.startsWith(state.apiURLString)) {
@@ -161,46 +139,23 @@ function createRequestModifier (getState, dnslinkResolver, ipfsPathValidator, ru
 
       // Special handling of requests made to API
       if (request.url.startsWith(state.apiURLString)) {
-        // Requests made by 'blessed' Web UI
-        // --------------------------------------------
-        // Goal: Web UI works without setting CORS at go-ipfs
-        // (Without this snippet go-ipfs will return HTTP 403 due to additional origin check on the backend)
-        const origin = originUrl(request)
-        if (origin && origin.startsWith(state.webuiRootUrl)) {
-          // console.log('onBeforeSendHeaders', request)
-          // console.log('onBeforeSendHeaders.origin', origin)
-          // Swap Origin to pass server-side check
-          // (go-ipfs returns HTTP 403 on origin mismatch if there are no CORS headers)
-          const swapOrigin = (at) => {
-            request.requestHeaders[at].value = request.requestHeaders[at].value.replace(state.gwURL.origin, state.apiURL.origin)
-          }
-          let foundAt = request.requestHeaders.findIndex(h => h.name === 'Origin')
-          if (foundAt > -1) swapOrigin(foundAt)
-          foundAt = request.requestHeaders.findIndex(h => h.name === 'Referer')
-          if (foundAt > -1) swapOrigin(foundAt)
-
-          // Save access-control-request-headers from preflight
-          foundAt = request.requestHeaders.findIndex(h => h.name && h.name.toLowerCase() === 'access-control-request-headers')
-          if (foundAt > -1) {
-            acrhHeaders.set(request.requestId, request.requestHeaders[foundAt].value)
-            // console.log('onBeforeSendHeaders FOUND access-control-request-headers', acrhHeaders.get(request.requestId))
-          }
-          // console.log('onBeforeSendHeaders fixed headers', request.requestHeaders)
-        }
-
         // '403 - Forbidden' fix for Chrome and Firefox
         // --------------------------------------------
-        // We remove Origin header from requests made to API URL and WebUI
-        // by js-ipfs-http-client running in WebExtension context to remove need
-        // for manual CORS whitelisting via Access-Control-Allow-Origin at go-ipfs
+        // We remove Origin header from requests made to API URL
+        // by js-ipfs-http-client running in WebExtension context.
+        // This act as unification of CORS behavior across all vendors,
+        // where behavior was non-deterministic and changed between releases.
+        // Without this, some users would need to do manual CORS whitelisting
+        // by adding webExtensionOrigin to API.Access-Control-Allow-Origin at their IPFS node.
         // More info:
         // Firefox: https://github.com/ipfs-shipyard/ipfs-companion/issues/622
         // Chromium 71: https://github.com/ipfs-shipyard/ipfs-companion/pull/616
         // Chromium 72: https://github.com/ipfs-shipyard/ipfs-companion/issues/630
         const isWebExtensionOrigin = (origin) => {
           // console.log(`origin=${origin}, webExtensionOrigin=${webExtensionOrigin}`)
-          // Chromium <= 71 returns opaque Origin as defined in
+          // Chromium <72 returns opaque Origin as defined in
           // https://html.spec.whatwg.org/multipage/origin.html#ascii-serialisation-of-an-origin
+          // TODO: remove this when <72 is not used by users
           if (origin == null || origin === 'null') {
             return true
           }
@@ -274,41 +229,6 @@ function createRequestModifier (getState, dnslinkResolver, ipfsPathValidator, ru
         return
       }
 
-      // Special handling of requests made to API
-      if (request.url.startsWith(state.apiURLString)) {
-        // Special handling of requests made by 'blessed' Web UI from local Gateway
-        // Goal: Web UI works without setting CORS at go-ipfs
-        // (This includes 'ignored' requests: CORS needs to be fixed even if no redirect is done)
-        const origin = originUrl(request)
-        if (origin && origin.startsWith(state.webuiRootUrl) && request.responseHeaders) {
-          // console.log('onHeadersReceived', request)
-          const acaOriginHeader = { name: 'Access-Control-Allow-Origin', value: state.gwURL.origin }
-          const foundAt = findHeaderIndex(acaOriginHeader.name, request.responseHeaders)
-          if (foundAt > -1) {
-            request.responseHeaders[foundAt].value = acaOriginHeader.value
-          } else {
-            request.responseHeaders.push(acaOriginHeader)
-          }
-
-          // Restore access-control-request-headers from preflight
-          const acrhValue = acrhHeaders.get(request.requestId)
-          if (acrhValue) {
-            const acahHeader = { name: 'Access-Control-Allow-Headers', value: acrhValue }
-            const foundAt = findHeaderIndex(acahHeader.name, request.responseHeaders)
-            if (foundAt > -1) {
-              request.responseHeaders[foundAt].value = acahHeader.value
-            } else {
-              request.responseHeaders.push(acahHeader)
-            }
-            acrhHeaders.del(request.requestId)
-            // console.log('onHeadersReceived SET  Access-Control-Allow-Headers', header)
-          }
-
-          // console.log('onHeadersReceived fixed headers', request.responseHeaders)
-          return { responseHeaders: request.responseHeaders }
-        }
-      }
-
       // Skip if request is marked as ignored
       if (isIgnored(request.requestId)) {
         return
@@ -317,6 +237,7 @@ function createRequestModifier (getState, dnslinkResolver, ipfsPathValidator, ru
       if (state.redirect) {
         // Late redirect as a workaround for edge cases such as:
         // - CORS XHR in Firefox: https://github.com/ipfs-shipyard/ipfs-companion/issues/436
+        // TODO: remove when Firefox with a fix landed in Stable channel
         if (onHeadersReceivedRedirect.has(request.requestId)) {
           onHeadersReceivedRedirect.delete(request.requestId)
           if (state.dnslinkPolicy) {
@@ -529,6 +450,25 @@ function normalizedUnhandledIpfsProtocol (request, pubGwUrl) {
   }
 }
 
-function findHeaderIndex (name, headers) {
-  return headers.findIndex(x => x.name && x.name.toLowerCase() === name.toLowerCase())
+/* not used at the moment, but this heuristic may be useful in the future
+// Note: Chrome 72+  requires 'extraHeaders' for access to Referer header
+const originUrls = new LRU(requestCacheCfg) // request.originUrl workaround for Chrome
+const originUrl = (request) => {
+  // Firefox and Chrome provide relevant value in different fields:
+  // (Firefox) request object includes full URL of origin document, return as-is
+  if (request.originUrl) return request.originUrl
+  // (Chrome) is lacking: `request.initiator` is just the origin (protocol+hostname+port)
+  // To reconstruct originUrl we read full URL from Referer header in onBeforeSendHeaders
+  // and cache it for short time
+  // TODO: when request.originUrl is available in Chrome the `originUrls` cache can be removed
+  const cachedUrl = originUrls.get(request.requestId)
+  if (cachedUrl) return cachedUrl
+  if (request.requestHeaders) {
+    const referer = request.requestHeaders.find(h => h.name === 'Referer')
+    if (referer) {
+      originUrls.set(request.requestId, referer.value)
+      return referer.value
+    }
+  }
 }
+*/
diff --git a/add-on/src/lib/state.js b/add-on/src/lib/state.js
index b9fa09503..92d5a0d24 100644
--- a/add-on/src/lib/state.js
+++ b/add-on/src/lib/state.js
@@ -4,10 +4,6 @@
 const { safeURL } = require('./options')
 const offlinePeerCount = -1
 
-// CID of a 'blessed' Web UI release
-// which should work without setting CORS headers
-const webuiCid = 'QmYTRvKFGhxgBiUreiw7ihn8g95tfJTWDt7aXqDsvAcJse' // v2.4.7
-
 function initState (options) {
   // we store options and some pregenerated values to avoid async storage
   // reads and minimize performance impact on overall browsing experience
@@ -26,11 +22,9 @@ function initState (options) {
   state.gwURLString = state.gwURL.toString()
   delete state.customGatewayUrl
   state.dnslinkPolicy = String(options.dnslinkPolicy) === 'false' ? false : options.dnslinkPolicy
-  state.webuiCid = webuiCid
-  state.webuiRootUrl = `${state.gwURLString}ipfs/${state.webuiCid}/`
+  state.webuiRootUrl = `${state.apiURLString}webui`
   return state
 }
 
 exports.initState = initState
 exports.offlinePeerCount = offlinePeerCount
-exports.webuiCid = webuiCid
diff --git a/test/functional/lib/ipfs-request-workarounds.test.js b/test/functional/lib/ipfs-request-workarounds.test.js
index 573c80c37..f9930608f 100644
--- a/test/functional/lib/ipfs-request-workarounds.test.js
+++ b/test/functional/lib/ipfs-request-workarounds.test.js
@@ -55,8 +55,9 @@ describe('modifyRequest processing', function () {
     })
   })
 
-  describe('a request to <apiURL>/api/v0/ with Origin=moz-extension://{extension-installation-id}', function () {
-    it('should remove Origin header with moz-extension://', async function () {
+  describe('a request to <apiURL>/api/v0/ made from extension with Origin header', function () {
+    it('should have it removed if Origin: moz-extension://{extension-installation-id}', async function () {
+      // Context: Firefox 65 started setting this header
       // set vendor-specific Origin for WebExtension context
       browser.runtime.getURL.withArgs('/').returns('moz-extension://0f334731-19e3-42f8-85e2-03dbf50026df/')
       // ensure clean modifyRequest
@@ -73,10 +74,8 @@ describe('modifyRequest processing', function () {
       expect(modifyRequest.onBeforeSendHeaders(request).requestHeaders).to.not.include(bogusOriginHeader)
       browser.runtime.getURL.flush()
     })
-  })
-
-  describe('a request to <apiURL>/api/v0/ with Origin=chrome-extension://{extension-installation-id}', function () {
-    it('should remove Origin header with chrome-extension://', async function () {
+    it('should have it removed if Origin: chrome-extension://{extension-installation-id}', async function () {
+      // Context: Chromium 72 started setting this header
       // set vendor-specific Origin for WebExtension context
       browser.runtime.getURL.withArgs('/').returns('chrome-extension://trolrorlrorlrol/')
       // ensure clean modifyRequest
@@ -93,10 +92,8 @@ describe('modifyRequest processing', function () {
       expect(modifyRequest.onBeforeSendHeaders(request).requestHeaders).to.not.include(bogusOriginHeader)
       browser.runtime.getURL.flush()
     })
-  })
-
-  describe('a request to <apiURL>/api/v0/ with Origin=null', function () {
-    it('should remove Origin header ', async function () {
+    it('should have it removed if Origin: null ', async function () {
+      // Context: Chromium <72 was setting this header
       // set vendor-specific Origin for WebExtension context
       browser.runtime.getURL.withArgs('/').returns(undefined)
       // ensure clean modifyRequest
@@ -115,50 +112,6 @@ describe('modifyRequest processing', function () {
     })
   })
 
-  // Web UI is loaded from hardcoded 'blessed' CID, which enables us to remove
-  // CORS limitation. This makes Web UI opened from browser action work without
-  // the need for any additional configuration of go-ipfs daemon
-  describe('a request to API from blessed webuiRootUrl', function () {
-    it('should pass without CORS limitations ', async function () {
-      // ensure clean modifyRequest
-      runtime = Object.assign({}, await createRuntimeChecks(browser)) // make it mutable for tests
-      modifyRequest = createRequestModifier(getState, dnslinkResolver, ipfsPathValidator, runtime)
-      // test
-      const webuiOriginHeader = { name: 'Origin', value: state.webuiRootUrl }
-      const webuiRefererHeader = { name: 'Referer', value: state.webuiRootUrl }
-      // CORS whitelisting does not worh in Chrome 72 without passing/restoring ACRH preflight header
-      const acrhHeader = { name: 'Access-Control-Request-Headers', value: 'X-Test' } // preflight to store
-
-      // Test request
-      let request = {
-        requestHeaders: [webuiOriginHeader, webuiRefererHeader, acrhHeader],
-        type: 'xmlhttprequest',
-        originUrl: state.webuiRootUrl,
-        url: `${state.apiURLString}api/v0/id`
-      }
-      request = modifyRequest.onBeforeRequest(request) || request // executes before onBeforeSendHeaders, may mutate state
-      const requestHeaders = modifyRequest.onBeforeSendHeaders(request).requestHeaders
-
-      // "originUrl" should be swapped to look like it came from the same origin as HTTP API
-      const expectedOriginUrl = state.webuiRootUrl.replace(state.gwURLString, state.apiURLString)
-      expect(requestHeaders).to.deep.include({ name: 'Origin', value: expectedOriginUrl })
-      expect(requestHeaders).to.deep.include({ name: 'Referer', value: expectedOriginUrl })
-      expect(requestHeaders).to.deep.include(acrhHeader)
-
-      // Test response
-      const response = Object.assign({}, request)
-      delete response.requestHeaders
-      response.responseHeaders = []
-      const responseHeaders = modifyRequest.onHeadersReceived(response).responseHeaders
-      const corsHeader = { name: 'Access-Control-Allow-Origin', value: state.gwURL.origin }
-      const acahHeader = { name: 'Access-Control-Allow-Headers', value: acrhHeader.value } // expect value restored from preflight
-      expect(responseHeaders).to.deep.include(corsHeader)
-      expect(responseHeaders).to.deep.include(acahHeader)
-
-      browser.runtime.getURL.flush()
-    })
-  })
-
   after(function () {
     delete global.URL
     delete global.browser