How to prevent users (except for admin / editors) from viewing canonical page for content type? For a certain content type, if you want most users to be able to view the content type (node bundle) when it's loaded on landing pages or on other node pages, but you don't want them to be able to access the route entity.node.canonical for the content type, unless they have an explicit permissions (which would be given to admin role and editor role), what is the best way to do this in Drupal 8?

As an example, let's say a site has a "video" content type, when it's loaded in a block, such as a related videos block, you want everyone to be able to view it, but if a user tries to go to that video's canonical page, they get a 404, unless they have the role with the permission to view it.

This would be for certain bundles of content or media entities that I want to only allow access to in a block, but the canonical page should 404 if the user doesn't have the correct permission.

What is the best strategy to set this up in Drupal 8?
In Drupal 7, I believe you could do this with Rules. The Rules module is technically available for Drupal 8, but I couldn't figure out how to use it, so I'll show you how to wire up a custom module that redirects manually based on whatever your criteria is.



The basic idea is, in order to do custom redirects, you have to register an EventSubscriber service, subscribe to REQUEST KernelEvent, and then you have access to the entire request object.

In my company's case, I wrote a custom module called "redirect", so that's what this example will use.

First, create a redirect.services.yml file.

services:
  redirect.subscriber:
    class: Drupal\redirect\EventSubscriber\RedirectSubscriber
    tags:
      - { name: event_subscriber}


Then, create the class referenced above (Drupal\redirect\EventSubscriber\RedirectSubscriber) in src/EventSubscriber/RedirectSubscriber.php.

&lt;?php

namespace Drupal\redirect\EventSubscriber;

use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Drupal\node\Entity\Node;

class RedirectSubscriber implements EventSubscriberInterface {

  public function redirect(GetResponseEvent $event) {
    $request = $event-&gt;getRequest();
    $baseUrl = $request-&gt;getBaseUrl();
    $attr = $request-&gt;attributes;
    $roles = \Drupal::currentUser()-&gt;getRoles();

    if ($attr !== null) {
      $node = $attr-&gt;get('node');
      if ($node !== null &amp;&amp; $node instanceof Node) {
        $type = $node-&gt;get('type')-&gt;getString();
        if ($type == 'seel_lesson_plan' &amp;&amp; !in_array('administrator', $roles)) {
          $event-&gt;setResponse(
            new RedirectResponse("$baseUrl/url-to-404-page")
          );
        }
      }
    }
  }

  static function getSubscribedEvents() {
    $events[KernelEvents::REQUEST][] = ['redirect'];
    return $events;
  }

}


To get a better idea of how this works, read an explanation of registering event subscribers, the official Events API page, and this other Drupal SE question about redirecting content pages.