Returning alternate HTTP codes for unpublished node I'm trying to return the 404 page instead of 403 response for unpublished nodes in Drupal 8.

I tested  kernel response subscriber, but found the code I was using would only change the status code to 404 from 403, not actually display the 404 page.  So maybe someone can show me how to generate a 404 page Response object there?

This is the code I was using:

class ResponseSubscriber implements EventSubscriberInterface {

  /**
   * {@inheritdoc}
   */
  public static function getSubscribedEvents() {
    return [KernelEvents::RESPONSE =&gt; [['alterResponse']]];
  }

  /**
   * Change status code to 404 from 403 if page is an unpublished node.
   *
   * @param FilterResponseEvent $event
   *   The route building event.
   */
  public function alterResponse(FilterResponseEvent $event) {
    if ($event-&gt;getResponse()-&gt;getStatusCode() == 403) {
      /** @var \Symfony\Component\HttpFoundation\Request $request */
      $request = $event-&gt;getRequest();
      $node = $request-&gt;attributes-&gt;get('node');
      if ($node instanceof Node &amp;&amp; !$node-&gt;isPublished()) {
        $response = $event-&gt;getResponse();
        // This changes the code, but doesn't return a 404 page.
        $response-&gt;setStatusCode(404);

        $event-&gt;setResponse($response);
      }
    }
  }

}


I finally resorted to removing this response subscriber completely and used hook_node_access like this:

use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Drupal\Core\Access\AccessResult;

function unpublished_404_node_access(\Drupal\node\NodeInterface $node, $op, \Drupal\Core\Session\AccountInterface $account) {

  if ($op == 'view' &amp;&amp; !$node-&gt;isPublished()) {
    if (\Drupal::moduleHandler()-&gt;moduleExists('workbench_moderation') &amp;&amp; $account-&gt;hasPermission('view any unpublished content')) {
      return AccessResult::neutral();
    }
    elseif (\Drupal::routeMatch()-&gt;getRouteName() == 'entity.node.canonical' &amp;&amp; \Drupal::routeMatch()-&gt;getRawParameter('node') == $node-&gt;id()) {
      throw new NotFoundHttpException();
      return AccessResult::neutral();
    }
  }

  return AccessResult::neutral();
}


This seems to be in line with several answers on this site for Drupal 7.  But I wanted to see if anyone has a better way of doing this either with a KernelEvent subscriber, rather than hook_node_access.  It seems like what I want to do is test if a node is returning a 403 and then generate a new response with the 404 page and 404 status code.  I'm not sure how to do that.
You can try to do this earlier in an exception instead of a response subscriber. Extend HttpExceptionSubscriberBase, so you need less code to do this. Then replace the 403 with a 404 exception with the method $event-&gt;setException()

/src/EventSubscriber/Unpublished404Subscriber.php

&lt;?php

namespace Drupal\mymodule\EventSubscriber;

use Drupal\Core\EventSubscriber\HttpExceptionSubscriberBase;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

class Unpublished404Subscriber extends HttpExceptionSubscriberBase {

  protected static function getPriority() {
    // set priority higher than 50 if you want to log "page not found"
    return 0;
  }

  protected function getHandledFormats() {
    return ['html'];
  }

  public function on403(GetResponseForExceptionEvent $event) {
    $request = $event-&gt;getRequest();
    if ($request-&gt;attributes-&gt;get('_route') == 'entity.node.canonical') {
      $event-&gt;setException(new NotFoundHttpException());
    }
  }

}


mymodule.services.yml:

services:
  mymodule.404:
    class: Drupal\mymodule\EventSubscriber\Unpublished404Subscriber
    arguments: []
    tags:
      - { name: event_subscriber }


This replaces all 403 exceptions for canonical node routes. You can get the node object $request-&gt;attributes-&gt;get('node') if you want to check if this is really because the node is unpublished.