    def _validate_next_url(self, next_url):
        """Validate next_url handling

        protects against external redirects, etc.

        Returns empty string if next_url is not considered safe,
        resulting in same behavior as if next_url is not specified.
        """
        # protect against some browsers' buggy handling of backslash as slash
        next_url = next_url.replace('\\', '%5C')
        public_url = self.settings.get("public_url")
        if public_url:
            proto = public_url.scheme
            host = public_url.netloc
        else:
            # guess from request
            proto = get_browser_protocol(self.request)
            host = self.request.host

        if next_url.startswith("///"):
            # strip more than 2 leading // down to 2
            # because urlparse treats that as empty netloc,
            # whereas browsers treat more than two leading // the same as //,
            # so netloc is the first non-/ bit
            next_url = "//" + next_url.lstrip("/")
        parsed_next_url = urlparse(next_url)

        if (next_url + '/').startswith(
            (
                f'{proto}://{host}/',
                f'//{host}/',
            )
        ) or (
            self.subdomain_host
            and parsed_next_url.netloc
            and ("." + parsed_next_url.netloc).endswith(
                "." + urlparse(self.subdomain_host).netloc
            )
        ):
            # treat absolute URLs for our host as absolute paths:
            # below, redirects that aren't strictly paths are rejected
            next_url = parsed_next_url.path
            if parsed_next_url.query:
                next_url = next_url + '?' + parsed_next_url.query
            if parsed_next_url.fragment:
                next_url = next_url + '#' + parsed_next_url.fragment
            parsed_next_url = urlparse(next_url)

        # if it still has host info, it didn't match our above check for *this* host
        if next_url and (parsed_next_url.netloc or not next_url.startswith('/')):
            self.log.warning("Disallowing redirect outside JupyterHub: %r", next_url)
            next_url = ''

        return next_url
Validate next_url handling

protects against external redirects, etc.

Returns empty string if next_url is not considered safe,
resulting in same behavior as if next_url is not specified.
jupyterhub/handlers/base.py