diff --git a/indra/newview/llchatbar.cpp b/indra/newview/llchatbar.cpp
index 3ee2c939614952bae05b16a2c704335a5c4a6361..96c707b08f25bb808b774a39a414842729989410 100644
--- a/indra/newview/llchatbar.cpp
+++ b/indra/newview/llchatbar.cpp
@@ -674,7 +674,7 @@ class LLChatHandler : public LLCommandHandler
 {
 public:
 	// not allowed from outside the app
-	LLChatHandler() : LLCommandHandler("chat", true) { }
+	LLChatHandler() : LLCommandHandler("chat", UNTRUSTED_BLOCK) { }
 
     // Your code here
 	bool handle(const LLSD& tokens, const LLSD& query_map,
diff --git a/indra/newview/llcommandhandler.cpp b/indra/newview/llcommandhandler.cpp
index a04182a910afc96291d6eb9c6fbd320c794ff1c1..af6488388a39add5cf462851bd0e9b490a79a1ba 100644
--- a/indra/newview/llcommandhandler.cpp
+++ b/indra/newview/llcommandhandler.cpp
@@ -38,12 +38,14 @@
 // system includes
 #include <boost/tokenizer.hpp>
 
+#define THROTTLE_PERIOD    15    // required secs between throttled commands
+
 //---------------------------------------------------------------------------
 // Underlying registry for command handlers, not directly accessible.
 //---------------------------------------------------------------------------
 struct LLCommandHandlerInfo
 {
-	bool mRequireTrustedBrowser;
+	LLCommandHandler::EUntrustedAccess mUntrustedBrowserAccess;
 	LLCommandHandler* mHandler;	// safe, all of these are static objects
 };
 
@@ -51,7 +53,9 @@ class LLCommandHandlerRegistry
 {
 public:
 	static LLCommandHandlerRegistry& instance();
-	void add(const char* cmd, bool require_trusted_browser, LLCommandHandler* handler);
+	void add(const char* cmd,
+			 LLCommandHandler::EUntrustedAccess untrusted_access,
+			 LLCommandHandler* handler);
 	bool dispatch(const std::string& cmd,
 				  const LLSD& params,
 				  const LLSD& query_map,
@@ -72,10 +76,12 @@ LLCommandHandlerRegistry& LLCommandHandlerRegistry::instance()
 	return instance;
 }
 
-void LLCommandHandlerRegistry::add(const char* cmd, bool require_trusted_browser, LLCommandHandler* handler)
+void LLCommandHandlerRegistry::add(const char* cmd,
+								   LLCommandHandler::EUntrustedAccess untrusted_access,
+								   LLCommandHandler* handler)
 {
 	LLCommandHandlerInfo info;
-	info.mRequireTrustedBrowser = require_trusted_browser;
+	info.mUntrustedBrowserAccess = untrusted_access;
 	info.mHandler = handler;
 
 	mMap[cmd] = info;
@@ -87,15 +93,37 @@ bool LLCommandHandlerRegistry::dispatch(const std::string& cmd,
 										LLMediaCtrl* web,
 										bool trusted_browser)
 {
+	static F64 last_throttle_time = 0.0;
+	F64 cur_time = 0.0;
 	std::map<std::string, LLCommandHandlerInfo>::iterator it = mMap.find(cmd);
 	if (it == mMap.end()) return false;
 	const LLCommandHandlerInfo& info = it->second;
-	if (!trusted_browser && info.mRequireTrustedBrowser)
+	if (!trusted_browser)
 	{
-		// block request from external browser, but report as
-		// "handled" because it was well formatted.
-		LL_WARNS_ONCE("SLURL") << "Blocked SLURL command from untrusted browser" << LL_ENDL;
-		return true;
+		switch (info.mUntrustedBrowserAccess)
+		{
+		case LLCommandHandler::UNTRUSTED_ALLOW:
+			// fall through and let the command be handled
+			break;
+
+		case LLCommandHandler::UNTRUSTED_BLOCK:
+			// block request from external browser, but report as
+			// "handled" because it was well formatted.
+			LL_WARNS_ONCE("SLURL") << "Blocked SLURL command from untrusted browser" << LL_ENDL;
+			return true;
+
+		case LLCommandHandler::UNTRUSTED_THROTTLE:
+			cur_time = LLTimer::getElapsedSeconds();
+			if (cur_time < last_throttle_time + THROTTLE_PERIOD)
+			{
+				// block request from external browser if it happened
+				// within THROTTLE_PERIOD secs of the last command
+				LL_WARNS_ONCE("SLURL") << "Throttled SLURL command from untrusted browser" << LL_ENDL;
+				return true;
+			}
+			last_throttle_time = cur_time;
+			break;
+		}
 	}
 	if (!info.mHandler) return false;
 	return info.mHandler->handle(params, query_map, web);
@@ -106,10 +134,9 @@ bool LLCommandHandlerRegistry::dispatch(const std::string& cmd,
 //---------------------------------------------------------------------------
 
 LLCommandHandler::LLCommandHandler(const char* cmd,
-								   bool require_trusted_browser)
+								   EUntrustedAccess untrusted_access)
 {
-	LLCommandHandlerRegistry::instance().add(
-			cmd, require_trusted_browser, this);
+	LLCommandHandlerRegistry::instance().add(cmd, untrusted_access, this);
 }
 
 LLCommandHandler::~LLCommandHandler()
diff --git a/indra/newview/llcommandhandler.h b/indra/newview/llcommandhandler.h
index 5cb3ee73d4014a9a474f44a095d20cbf7706c5cd..1bae6d94142545c973d7b141cae2f83f8172d2f8 100644
--- a/indra/newview/llcommandhandler.h
+++ b/indra/newview/llcommandhandler.h
@@ -43,7 +43,7 @@ class LLFooHandler : public LLCommandHandler
     // Inform the system you handle commands starting
 	// with "foo" and they are only allowed from
 	// "trusted" (pointed at Linden content) browsers
-	LLFooHandler() : LLCommandHandler("foo", true) { }
+	LLFooHandler() : LLCommandHandler("foo", UNTRUSTED_BLOCK) { }
 
     // Your code here
 	bool handle(const LLSD& tokens, const LLSD& query_map,
@@ -65,7 +65,14 @@ class LLMediaCtrl;
 class LLCommandHandler
 {
 public:
-	LLCommandHandler(const char* command, bool allow_from_untrusted_browser);
+	enum EUntrustedAccess
+	{
+		UNTRUSTED_ALLOW,       // allow commands from untrusted browsers
+		UNTRUSTED_BLOCK,       // ignore commands from untrusted browsers
+		UNTRUSTED_THROTTLE     // allow untrusted, but only a few per min.
+	};
+
+	LLCommandHandler(const char* command, EUntrustedAccess untrusted_access);
 		// Automatically registers object to get called when 
 		// command is executed.  All commands can be processed
 		// in links from LLMediaCtrl, but some (like teleport)
diff --git a/indra/newview/llfloaterhandler.h b/indra/newview/llfloaterhandler.h
index 31ea80c12c1594d493a9058d167b2d19f545998d..cd9d8b5377cab6b2b9af04f34c5fa73baaa2f1ca 100644
--- a/indra/newview/llfloaterhandler.h
+++ b/indra/newview/llfloaterhandler.h
@@ -38,7 +38,7 @@ class LLFloaterHandler
 :	public LLCommandHandler
 {
 public:
-	LLFloaterHandler() : LLCommandHandler("floater", true) { }
+	LLFloaterHandler() : LLCommandHandler("floater", UNTRUSTED_BLOCK) { }
 	bool handle(const LLSD& params, const LLSD& query_map, LLMediaCtrl* web);
 };
 
diff --git a/indra/newview/llfloaterparcel.cpp b/indra/newview/llfloaterparcel.cpp
index 44270683a022456466a40a784b72165b736f73cd..88a39a495fd6702cbd733c665af033ac8ccfc637 100644
--- a/indra/newview/llfloaterparcel.cpp
+++ b/indra/newview/llfloaterparcel.cpp
@@ -53,7 +53,7 @@ class LLParcelHandler : public LLCommandHandler
 {
 public:
 	// requires trusted browser to trigger
-	LLParcelHandler() : LLCommandHandler("parcel", true) { }
+	LLParcelHandler() : LLCommandHandler("parcel", UNTRUSTED_THROTTLE) { }
 	bool handle(const LLSD& params, const LLSD& query_map,
 				LLMediaCtrl* web)
 	{
diff --git a/indra/newview/llgroupactions.cpp b/indra/newview/llgroupactions.cpp
index 10a17476d980f1b5c3724b0599106bd5dc6c9c81..d1cbe96906065f6fd70e5afb978def582b7c931b 100644
--- a/indra/newview/llgroupactions.cpp
+++ b/indra/newview/llgroupactions.cpp
@@ -51,7 +51,7 @@ class LLGroupHandler : public LLCommandHandler
 {
 public:
 	// requires trusted browser to trigger
-	LLGroupHandler() : LLCommandHandler("group", true) { }
+	LLGroupHandler() : LLCommandHandler("group", UNTRUSTED_THROTTLE) { }
 	bool handle(const LLSD& tokens, const LLSD& query_map,
 				LLMediaCtrl* web)
 	{
diff --git a/indra/newview/llloginhandler.h b/indra/newview/llloginhandler.h
index 0844b80c7c6ab47111f386afb7e9a9d5044690da..d36ceaf3cc05564cb88c913bb03ddbafb2d103cc 100644
--- a/indra/newview/llloginhandler.h
+++ b/indra/newview/llloginhandler.h
@@ -39,7 +39,7 @@ class LLLoginHandler : public LLCommandHandler
 {
  public:
 	// allow from external browsers
-	LLLoginHandler() : LLCommandHandler("login", false) { }
+	LLLoginHandler() : LLCommandHandler("login", UNTRUSTED_ALLOW) { }
 	/*virtual*/ bool handle(const LLSD& tokens, const LLSD& query_map, LLMediaCtrl* web);
 
 	// Fill in our internal fields from a SLURL like
diff --git a/indra/newview/llmediactrl.cpp b/indra/newview/llmediactrl.cpp
index 62b38f2b4ac3cc3a9260794cfde0659b63b1ffa6..09a7edaa434c3fa4a949e82362dda2f0d8c3922e 100644
--- a/indra/newview/llmediactrl.cpp
+++ b/indra/newview/llmediactrl.cpp
@@ -916,15 +916,8 @@ void LLMediaCtrl::onClickLinkHref( LLPluginClassMedia* self )
 // 
 void LLMediaCtrl::onClickLinkNoFollow( LLPluginClassMedia* self )
 {
+	// let the dispatcher handle blocking/throttling of SLURLs
 	std::string url = self->getClickURL();
-	if (LLSLURL::isSLURLCommand(url)
-		&& !mTrusted)
-	{
-		// block handling of this secondlife:///app/ URL
-		LLNotifications::instance().add("UnableToOpenCommandURL");
-		return;
-	}
-
 	LLURLDispatcher::dispatch(url, this, mTrusted);
 }
 
diff --git a/indra/newview/llnearbychatbar.cpp b/indra/newview/llnearbychatbar.cpp
index 764e093bcc3109e7f88f09eac6efd48bff21bf4e..e348189ea975d1c7f291fd1c953c3ba82d3248d2 100644
--- a/indra/newview/llnearbychatbar.cpp
+++ b/indra/newview/llnearbychatbar.cpp
@@ -616,7 +616,7 @@ class LLChatHandler : public LLCommandHandler
 {
 public:
 	// not allowed from outside the app
-	LLChatHandler() : LLCommandHandler("chat", true) { }
+	LLChatHandler() : LLCommandHandler("chat", UNTRUSTED_BLOCK) { }
 
     // Your code here
 	bool handle(const LLSD& tokens, const LLSD& query_map,
diff --git a/indra/newview/llpanelclassified.cpp b/indra/newview/llpanelclassified.cpp
index c77d089af7ccec2efcb284dd1c8ef9bc2246358e..ee5d2652203712d7e9a0e253b956a77d68bce9a2 100644
--- a/indra/newview/llpanelclassified.cpp
+++ b/indra/newview/llpanelclassified.cpp
@@ -119,7 +119,7 @@ class LLClassifiedTeleportHandler : public LLCommandHandler
 {
 public:
 	// don't allow from external browsers because it moves you immediately
-	LLClassifiedTeleportHandler() : LLCommandHandler("classifiedteleport", true) { }
+	LLClassifiedTeleportHandler() : LLCommandHandler("classifiedteleport", UNTRUSTED_BLOCK) { }
 
 	bool handle(const LLSD& tokens, const LLSD& queryMap)
 	{
diff --git a/indra/newview/llpanellogin.cpp b/indra/newview/llpanellogin.cpp
index ef830d5f03d8540fc4b57266714d32d8283e94ea..9caa751854215a157dba25f0735672e731a26e7c 100644
--- a/indra/newview/llpanellogin.cpp
+++ b/indra/newview/llpanellogin.cpp
@@ -87,7 +87,7 @@ class LLLoginRefreshHandler : public LLCommandHandler
 {
 public:
 	// don't allow from external browsers
-	LLLoginRefreshHandler() : LLCommandHandler("login_refresh", true) { }
+	LLLoginRefreshHandler() : LLCommandHandler("login_refresh", UNTRUSTED_BLOCK) { }
 	bool handle(const LLSD& tokens, const LLSD& query_map, LLMediaCtrl* web)
 	{	
 		if (LLStartUp::getStartupState() < STATE_LOGIN_CLEANUP)
diff --git a/indra/newview/llpanelprofile.cpp b/indra/newview/llpanelprofile.cpp
index 6d3d307526baf59c93bfba741a08d58dd5ea4772..ce01568e99111dfa970fdca1503c07826be7826e 100644
--- a/indra/newview/llpanelprofile.cpp
+++ b/indra/newview/llpanelprofile.cpp
@@ -46,7 +46,7 @@ class LLAgentHandler : public LLCommandHandler
 {
 public:
 	// requires trusted browser to trigger
-	LLAgentHandler() : LLCommandHandler("agent", true) { }
+	LLAgentHandler() : LLCommandHandler("agent", UNTRUSTED_THROTTLE) { }
 
 	bool handle(const LLSD& params, const LLSD& query_map,
 		LLMediaCtrl* web)
diff --git a/indra/newview/llstatusbar.cpp b/indra/newview/llstatusbar.cpp
index c724fb5315ac580b50a45d3596521c8b41a327f9..40b8445dcfbb66350f8c485cf0b07ab1fcf19831 100644
--- a/indra/newview/llstatusbar.cpp
+++ b/indra/newview/llstatusbar.cpp
@@ -623,7 +623,7 @@ class LLBalanceHandler : public LLCommandHandler
 {
 public:
 	// Requires "trusted" browser/URL source
-	LLBalanceHandler() : LLCommandHandler("balance", true) { }
+	LLBalanceHandler() : LLCommandHandler("balance", UNTRUSTED_BLOCK) { }
 	bool handle(const LLSD& tokens, const LLSD& query_map, LLMediaCtrl* web)
 	{
 		if (tokens.size() == 1
diff --git a/indra/newview/llurldispatcher.cpp b/indra/newview/llurldispatcher.cpp
index 4cdeca7707f29f13bac930b6cf7527a8e650a422..901d0594f12847dcfa1c71ed9130c1c78ed3581e 100644
--- a/indra/newview/llurldispatcher.cpp
+++ b/indra/newview/llurldispatcher.cpp
@@ -331,7 +331,7 @@ class LLTeleportHandler : public LLCommandHandler
 	// Teleport requests *must* come from a trusted browser
 	// inside the app, otherwise a malicious web page could
 	// cause a constant teleport loop.  JC
-	LLTeleportHandler() : LLCommandHandler("teleport", true) { }
+	LLTeleportHandler() : LLCommandHandler("teleport", UNTRUSTED_BLOCK) { }
 
 	bool handle(const LLSD& tokens, const LLSD& query_map,
 				LLMediaCtrl* web)
diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml
index 1f0578f81925ed39f52ecec33abd4e7e75df8120..ea1c0dd99bd29b9af6e5c42262764450f8579fa3 100644
--- a/indra/newview/skins/default/xui/en/notifications.xml
+++ b/indra/newview/skins/default/xui/en/notifications.xml
@@ -6493,14 +6493,6 @@ Click Accept to join the chat or Decline to decline the invitation. Click Block
     You just entered a region using a different server version, which may affect performance. Click to see the release notes.
   </notification>
 
-  <notification
-   icon="notifytip.tga"
-   name="UnableToOpenCommandURL"
-   priority="high"
-   type="notifytip">
-    The link you clicked cannot be opened from this web browser.
-  </notification>
-
   <notification
    icon="notifytip.tga"
    name="UnsupportedCommandSLURL"