diff --git a/core/lib/Drupal/Core/EventSubscriber/MaintenanceModeSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/MaintenanceModeSubscriber.php
index bda3291846..fc67a45c54 100644
--- a/core/lib/Drupal/Core/EventSubscriber/MaintenanceModeSubscriber.php
+++ b/core/lib/Drupal/Core/EventSubscriber/MaintenanceModeSubscriber.php
@@ -3,6 +3,7 @@
 namespace Drupal\Core\EventSubscriber;
 
 use Drupal\Component\Render\FormattableMarkup;
+use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Config\ConfigFactoryInterface;
 use Drupal\Core\Render\BareHtmlPageRendererInterface;
 use Drupal\Core\Messenger\MessengerInterface;
@@ -12,7 +13,9 @@
 use Drupal\Core\Site\MaintenanceModeInterface;
 use Drupal\Core\StringTranslation\StringTranslationTrait;
 use Drupal\Core\StringTranslation\TranslationInterface;
+use Drupal\jsonapi\Routing\Routes;
 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use Symfony\Component\HttpFoundation\JsonResponse;
 use Symfony\Component\HttpFoundation\Response;
 use Symfony\Component\HttpKernel\Event\GetResponseEvent;
 use Symfony\Component\HttpKernel\KernelEvents;
@@ -110,17 +113,31 @@ public function onKernelRequestMaintenance(GetResponseEvent $event) {
       if (!$this->maintenanceMode->exempt($this->account)) {
         // Deliver the 503 page if the site is in maintenance mode and the
         // logged in user is not allowed to bypass it.
+        $message = $this->getSiteMaintenanceMessage();
+        $message_no_html = strip_tags($message);
 
         // If the request format is not 'html' then show default maintenance
         // mode page else show a text/plain page with maintenance message.
         if ($request->getRequestFormat() !== 'html') {
-          $response = new Response($this->getSiteMaintenanceMessage(), 503, ['Content-Type' => 'text/plain']);
-          $event->setResponse($response);
-          return;
+          // Handle a special case for the JSON:API module.
+          if ($route_match->getRouteObject()->getDefault(Routes::JSON_API_ROUTE_FLAG_KEY)) {
+            $data = ["errors" => ["status" => "503 Service Unavailable", "detail" => $message_no_html]];
+            $response = new JsonResponse($data, 503, ['Content-Type' => 'application/vnd.api+json']);
+          }
+          else {
+            $response = new Response($message, 503, ['Content-Type' => 'text/plain']);
+          }
         }
-        drupal_maintenance_theme();
-        $response = $this->bareHtmlPageRenderer->renderBarePage(['#markup' => $this->getSiteMaintenanceMessage()], $this->t('Site under maintenance'), 'maintenance_page');
-        $response->setStatusCode(503);
+        else {
+          drupal_maintenance_theme();
+          $response = $this->bareHtmlPageRenderer->renderBarePage(['#markup' => $message], $this->t('Site under maintenance'), 'maintenance_page');
+          $response->setStatusCode(503);
+        }
+
+        // Always set the plain text message as a header as well.
+        // A 4k headers length limit is a default in many web servers.
+        $response->headers->set("X-Maintenance-Mode", Unicode::truncate($message_no_html, 4000, TRUE));
+
         $event->setResponse($response);
       }
       else {
