diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 1a0030bc12..632a8c78ae 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -3081,7 +3081,22 @@ void Application::showLoginScreen() {
 #endif
 }
 
+static const QUrl AUTHORIZED_EXTERNAL_QML_SOURCE { "https://content.highfidelity.com/Experiences/Releases" };
+
 void Application::initializeUi() {
+
+    // Allow remote QML content from trusted sources ONLY
+    {
+        auto defaultUrlValidator = OffscreenQmlSurface::getUrlValidator();
+        auto newValidator = [=](const QUrl& url)->bool {
+            if (AUTHORIZED_EXTERNAL_QML_SOURCE.isParentOf(url)) {
+                return true;
+            }
+            return defaultUrlValidator(url);
+        };
+        OffscreenQmlSurface::setUrlValidator(newValidator);
+    }
+
     AddressBarDialog::registerType();
     ErrorDialog::registerType();
     LoginDialog::registerType();
diff --git a/libraries/qml/src/qml/OffscreenSurface.cpp b/libraries/qml/src/qml/OffscreenSurface.cpp
index 69e6c833ee..117ff61c6e 100644
--- a/libraries/qml/src/qml/OffscreenSurface.cpp
+++ b/libraries/qml/src/qml/OffscreenSurface.cpp
@@ -23,6 +23,7 @@
 
 #include <gl/OffscreenGLCanvas.h>
 #include <shared/ReadWriteLockable.h>
+#include <NetworkingConstants.h>
 
 #include "Logging.h"
 #include "impl/SharedObject.h"
@@ -33,6 +34,23 @@
 using namespace hifi::qml;
 using namespace hifi::qml::impl;
 
+QmlUrlValidator OffscreenSurface::validator = [](const QUrl& url) -> bool { 
+    if (url.isRelative()) {
+        return true;
+    }
+
+    if (url.isLocalFile()) {
+        return true;
+    }
+
+    if (url.scheme() == URL_SCHEME_QRC) {
+        return true;
+    }
+
+    // By default, only allow local QML, either from the local filesystem or baked into the QRC
+    return false;
+};
+
 static uvec2 clampSize(const uvec2& size, uint32_t maxDimension) {
     return glm::clamp(size, glm::uvec2(1), glm::uvec2(maxDimension));
 }
@@ -307,6 +325,10 @@ void OffscreenSurface::loadInternal(const QUrl& qmlSource,
     // For desktop toolbar mode window: stop script when window is closed.
     if (qmlSource.isEmpty()) {
         getSurfaceContext()->engine()->quit();
+    }
+
+    if (!validator(qmlSource)) {
+        qCWarning(qmlLogging) << "Unauthorized QML URL found" << qmlSource;
         return;
     }
 
diff --git a/libraries/qml/src/qml/OffscreenSurface.h b/libraries/qml/src/qml/OffscreenSurface.h
index 18d24c93f7..528baf76be 100644
--- a/libraries/qml/src/qml/OffscreenSurface.h
+++ b/libraries/qml/src/qml/OffscreenSurface.h
@@ -40,6 +40,7 @@ class SharedObject;
 
 using QmlContextCallback = ::std::function<void(QQmlContext*)>;
 using QmlContextObjectCallback = ::std::function<void(QQmlContext*, QQuickItem*)>;
+using QmlUrlValidator = std::function<bool(const QUrl&)>;
 
 class OffscreenSurface : public QObject {
     Q_OBJECT
@@ -47,10 +48,13 @@ class OffscreenSurface : public QObject {
 public:
     static const QmlContextObjectCallback DEFAULT_CONTEXT_OBJECT_CALLBACK;
     static const QmlContextCallback DEFAULT_CONTEXT_CALLBACK;
-
+    static QmlUrlValidator validator;
     using TextureAndFence = std::pair<uint32_t, void*>;
     using MouseTranslator = std::function<QPoint(const QPointF&)>;
 
+
+    static const QmlUrlValidator& getUrlValidator() { return validator; }
+    static void setUrlValidator(const QmlUrlValidator& newValidator) { validator = newValidator; }
     static void setSharedContext(QOpenGLContext* context);
 
     OffscreenSurface();