diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -1613,6 +1613,7 @@
     'PhabricatorAuthLinkController' => 'applications/auth/controller/PhabricatorAuthLinkController.php',
     'PhabricatorAuthListController' => 'applications/auth/controller/config/PhabricatorAuthListController.php',
     'PhabricatorAuthLoginController' => 'applications/auth/controller/PhabricatorAuthLoginController.php',
+    'PhabricatorAuthLoginHandler' => 'applications/auth/handler/PhabricatorAuthLoginHandler.php',
     'PhabricatorAuthManagementCachePKCS8Workflow' => 'applications/auth/management/PhabricatorAuthManagementCachePKCS8Workflow.php',
     'PhabricatorAuthManagementLDAPWorkflow' => 'applications/auth/management/PhabricatorAuthManagementLDAPWorkflow.php',
     'PhabricatorAuthManagementListFactorsWorkflow' => 'applications/auth/management/PhabricatorAuthManagementListFactorsWorkflow.php',
@@ -5458,6 +5459,7 @@
     'PhabricatorAuthLinkController' => 'PhabricatorAuthController',
     'PhabricatorAuthListController' => 'PhabricatorAuthProviderConfigController',
     'PhabricatorAuthLoginController' => 'PhabricatorAuthController',
+    'PhabricatorAuthLoginHandler' => 'Phobject',
     'PhabricatorAuthManagementCachePKCS8Workflow' => 'PhabricatorAuthManagementWorkflow',
     'PhabricatorAuthManagementLDAPWorkflow' => 'PhabricatorAuthManagementWorkflow',
     'PhabricatorAuthManagementListFactorsWorkflow' => 'PhabricatorAuthManagementWorkflow',
diff --git a/src/applications/auth/controller/PhabricatorAuthStartController.php b/src/applications/auth/controller/PhabricatorAuthStartController.php
--- a/src/applications/auth/controller/PhabricatorAuthStartController.php
+++ b/src/applications/auth/controller/PhabricatorAuthStartController.php
@@ -163,8 +163,22 @@
         $button_columns);
     }
 
-    $login_message = PhabricatorEnv::getEnvConfig('auth.login-message');
-    $login_message = phutil_safe_html($login_message);
+    $handlers = PhabricatorAuthLoginHandler::getAllHandlers();
+
+    $delegating_controller = $this->getDelegatingController();
+
+    $header = array();
+    foreach ($handlers as $handler) {
+      $handler = clone $handler;
+
+      $handler->setRequest($request);
+
+      if ($delegating_controller) {
+        $handler->setDelegatingController($delegating_controller);
+      }
+
+      $header[] = $handler->getAuthLoginHeaderContent();
+    }
 
     $invite_message = null;
     if ($invite) {
@@ -178,7 +192,7 @@
     return $this->buildApplicationPage(
       array(
         $crumbs,
-        $login_message,
+        $header,
         $invite_message,
         $out,
       ),
diff --git a/src/applications/auth/handler/PhabricatorAuthLoginHandler.php b/src/applications/auth/handler/PhabricatorAuthLoginHandler.php
new file mode 100644
--- /dev/null
+++ b/src/applications/auth/handler/PhabricatorAuthLoginHandler.php
@@ -0,0 +1,36 @@
+<?php
+
+abstract class PhabricatorAuthLoginHandler
+  extends Phobject {
+
+  private $request;
+  private $delegatingController;
+
+  public function getAuthLoginHeaderContent() {
+    return array();
+  }
+
+  final public function setDelegatingController(AphrontController $controller) {
+    $this->delegatingController = $controller;
+    return $this;
+  }
+
+  final public function getDelegatingController() {
+    return $this->delegatingController;
+  }
+
+  final public function setRequest(AphrontRequest $request) {
+    $this->request = $request;
+    return $this;
+  }
+
+  final public function getRequest() {
+    return $this->request;
+  }
+
+  final public static function getAllHandlers() {
+    return id(new PhutilClassMapQuery())
+      ->setAncestorClass(__CLASS__)
+      ->execute();
+  }
+}
diff --git a/src/applications/config/check/PhabricatorExtraConfigSetupCheck.php b/src/applications/config/check/PhabricatorExtraConfigSetupCheck.php
--- a/src/applications/config/check/PhabricatorExtraConfigSetupCheck.php
+++ b/src/applications/config/check/PhabricatorExtraConfigSetupCheck.php
@@ -276,6 +276,10 @@
         'Impersonating users over the API is no longer supported.'),
 
       'feed.public' => pht('The framable public feed is no longer supported.'),
+
+      'auth.login-message' => pht(
+        'This configuration option has been replaced with a modular '.
+        'handler. See T9346.'),
     );
 
     return $ancient_config;
diff --git a/src/applications/config/option/PhabricatorAuthenticationConfigOptions.php b/src/applications/config/option/PhabricatorAuthenticationConfigOptions.php
--- a/src/applications/config/option/PhabricatorAuthenticationConfigOptions.php
+++ b/src/applications/config/option/PhabricatorAuthenticationConfigOptions.php
@@ -73,14 +73,6 @@
         ->addExample(
           "yourcompany.com\nmail.yourcompany.com",
           pht('Valid Setting')),
-      $this->newOption('auth.login-message', 'string', null)
-        ->setLocked(true)
-        ->setSummary(pht('A block of HTML displayed on the login screen.'))
-        ->setDescription(
-          pht(
-            "You can provide an arbitrary block of HTML here, which will ".
-            "appear on the login screen. Normally, you'd use this to provide ".
-            "login or registration instructions to users.")),
       $this->newOption('account.editable', 'bool', true)
         ->setBoolOptions(
           array(