Clean URL pagination (rewrite ?page=parameter) One of my client works with an SEO agency and they ask me to remove the pagination parameter ?page=x (generated by the Views module) and rewrite it like this:

Before:

/my-page?page=1


After:

/my-page-1


Is there a way to do it with Drupal? I don't see any option in the view or in Pathauto to do this.

Is it possible to handle this with a custom module or should I work with .htaccess file?

I've tried the Clean Pagination module but you have to manually set pages where clean pagination should work and the module doesn't seem to work properly.
You should write a PathProcessor that implements:


Drupal\Core\PathProcessor\InboundPathProcessorInterface
Drupal\Core\PathProcessor\OutboundPathProcessorInterface


Example:

file: modules/custom/mymodule/src/PathProcessor/MymodulePathProcessor.php

namespace Drupal\mymodule\PathProcessor;

use Drupal\Core\PathProcessor\OutboundPathProcessorInterface;
use Symfony\Component\HttpFoundation\Request;
use Drupal\Core\PathProcessor\InboundPathProcessorInterface;
use Drupal\Core\Render\BubbleableMetadata;

class MymodulePathProcessor implements InboundPathProcessorInterface, OutboundPathProcessorInterface {

  public function processInbound($path, Request $request) {
    if (strpos($path, '/my-page-') !== 0) {
       return $path;
    }
    $page =  preg_replace("/^\/my-page-(\d+)/", '$1', $path);
      if (ctype_digit($page)) {
        \Drupal::request()-&gt;query-&gt;set('page', $page);
        return "/my-page";
    }
    else {
      \Drupal::request()-&gt;query-&gt;set('page', 0);
      return "/my-page";
    }
    return $path;
  }

  public function processOutbound($path, &amp;$options = [], Request $request = NULL, BubbleableMetadata $bubbleable_metadata = NULL) {
    if ($path === '/my-page' &amp;&amp; isset($options['query']['page'])) {
      $page = $options['query']['page'];
      unset($options['query']['page']);
      return '/my-page-' . $page;
    }
    return $path;
  }

}


file: modules/custom/mymodule/mymodule.services.yml

services:
  mymodule.path_processor:
    class: Drupal\mymodule\PathProcessor\MymodulePathProcessor
    tags:
    - { name: path_processor_outbound}
    - { name: path_processor_inbound}


We also need to do a custom alteration to the pager. For the full pager the links are not created as the usual link render elements, so they don't go through the processOutboud, instead the twig file (classy/templates/navigation/pager.html.twig) uses a custom format. The next hook function does the adjustment.

file: modules/custom/mymodule/mymodule.module

/**
 * Implements hook_preprocess_HOOK().
 * Implements hook_preprocess_pager().
 *
 * @param $variables
 */
function mymodule_preprocess_pager(&amp;$variables) {
  $current_path = \Drupal::service('path.current')-&gt;getPath();
  if ($current_path === '/my-page')
  foreach ($variables['items']['pages'] as &amp;$page_link) {
    $page =  preg_replace("/^\?page=(\d+)/", '$1', $page_link['href']);
    $page_link['href'] = 'my-page-' . $page;
  }
}