title: Remove hash (#) from URL in Jhipster (both java and angular 6)
body: <p>I am using Jhipster Spring boot + angular 6. But i'm having trouble because of the hash(#) in URL. It is affecting SEO.</p>
  
  <p>I tried setting <code>useHash: false</code> in <code>app-routing-module.ts</code>.
  But then the API is not working when I run the project via <code>npm start</code>.</p>
  
  <p>I think somewhere in Java files I have to change a configuration to remove <code>#</code> from the URL.</p>
  
  <p>Here is my WebConfigurer code,</p>
  
  <pre><code>@Configuration
  public class WebConfigurer implements ServletContextInitializer, WebServerFactoryCustomizer&lt;WebServerFactory&gt; {
  
      private final Logger log = LoggerFactory.getLogger(WebConfigurer.class);
  
      private final Environment env;
  
      private final JHipsterProperties jHipsterProperties;
  
      private MetricRegistry metricRegistry;
  
      public WebConfigurer(Environment env, JHipsterProperties jHipsterProperties) {
  
          this.env = env;
          this.jHipsterProperties = jHipsterProperties;
      }
  
      @Override
      public void onStartup(ServletContext servletContext) throws ServletException {
          if (env.getActiveProfiles().length != 0) {
              log.info("Web application configuration, using profiles: {}", (Object[]) env.getActiveProfiles());
          }
          EnumSet&lt;DispatcherType&gt; disps = EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.ASYNC);
          initMetrics(servletContext, disps);
          log.info("Web application fully configured");
      }
  
      /**
       * Customize the Servlet engine: Mime types, the document root, the cache.
       */
      @Override
      public void customize(WebServerFactory server) {
          setMimeMappings(server);
  
          /*
           * Enable HTTP/2 for Undertow - https://twitter.com/ankinson/status/829256167700492288
           * HTTP/2 requires HTTPS, so HTTP requests will fallback to HTTP/1.1.
           * See the JHipsterProperties class and your application-*.yml configuration files
           * for more information.
           */
          if (jHipsterProperties.getHttp().getVersion().equals(JHipsterProperties.Http.Version.V_2_0) &amp;&amp;
              server instanceof UndertowServletWebServerFactory) {
  
              ((UndertowServletWebServerFactory) server)
                  .addBuilderCustomizers(builder -&gt;
                      builder.setServerOption(UndertowOptions.ENABLE_HTTP2, true));
          }
      }
  
      private void setMimeMappings(WebServerFactory server) {
          if (server instanceof ConfigurableServletWebServerFactory) {
              MimeMappings mappings = new MimeMappings(MimeMappings.DEFAULT);
              // IE issue, see https://github.com/jhipster/generator-jhipster/pull/711
              mappings.add("html", MediaType.TEXT_HTML_VALUE + ";charset=" + StandardCharsets.UTF_8.name().toLowerCase());
              // CloudFoundry issue, see https://github.com/cloudfoundry/gorouter/issues/64
              mappings.add("json", MediaType.TEXT_HTML_VALUE + ";charset=" + StandardCharsets.UTF_8.name().toLowerCase());
              ConfigurableServletWebServerFactory servletWebServer = (ConfigurableServletWebServerFactory) server;
              servletWebServer.setMimeMappings(mappings);
          }
      }
  
      /**
       * Initializes Metrics.
       */
      private void initMetrics(ServletContext servletContext, EnumSet&lt;DispatcherType&gt; disps) {
          log.debug("Initializing Metrics registries");
          servletContext.setAttribute(InstrumentedFilter.REGISTRY_ATTRIBUTE,
              metricRegistry);
          servletContext.setAttribute(MetricsServlet.METRICS_REGISTRY,
              metricRegistry);
  
          log.debug("Registering Metrics Filter");
          FilterRegistration.Dynamic metricsFilter = servletContext.addFilter("webappMetricsFilter",
              new InstrumentedFilter());
  
          metricsFilter.addMappingForUrlPatterns(disps, true, "/*");
          metricsFilter.setAsyncSupported(true);
  
          log.debug("Registering Metrics Servlet");
          ServletRegistration.Dynamic metricsAdminServlet =
              servletContext.addServlet("metricsServlet", new MetricsServlet());
  
          metricsAdminServlet.addMapping("/management/metrics/*");
          metricsAdminServlet.setAsyncSupported(true);
          metricsAdminServlet.setLoadOnStartup(2);
      }
  
      @Bean
      public CorsFilter corsFilter() {
          UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
          CorsConfiguration config = jHipsterProperties.getCors();
          if (config.getAllowedOrigins() != null &amp;&amp; !config.getAllowedOrigins().isEmpty()) {
              log.debug("Registering CORS filter");
              source.registerCorsConfiguration("/api/**", config);
              source.registerCorsConfiguration("/management/**", config);
              source.registerCorsConfiguration("/v2/api-docs", config);
          }
          return new CorsFilter(source);
      }
  
      @Autowired(required = false)
      public void setMetricRegistry(MetricRegistry metricRegistry) {
          this.metricRegistry = metricRegistry;
      }
  }
  </code></pre>
  
  <p>Here is my AngularRouteFilter servlet code,</p>
  
  <pre><code>public class AngularRouteFilter extends OncePerRequestFilter {
  
      // add the values you want to redirect for
      private static final Pattern PATTERN = Pattern.compile("^/((api|swagger-ui|management|swagger-resources)/|favicon\\.ico|v2/api-docs).*");
  
      @Override
      protected void doFilterInternal(HttpServletRequest request,
                                      HttpServletResponse response,
                                      FilterChain filterChain)
          throws ServletException, IOException {
          if (isServerRoute(request)) {
              filterChain.doFilter(request, response);
          } else {
              RequestDispatcher rd = request.getRequestDispatcher("/");
              rd.forward(request, response);
          }
      }
  
      protected static boolean isServerRoute(HttpServletRequest request) {
          if (request.getMethod().equals("GET")) {
              String uri = request.getRequestURI();
          if (uri.startsWith("/app")){
  
                  return true;
              }
              return PATTERN.matcher(uri).matches();
          }
          return true;
      }
  }
  </code></pre>
  
  <p>here is my Swagger index.html(swagger-ui/index.html)</p>
  
  <pre><code>&lt;!DOCTYPE html&gt;
  &lt;html&gt;
  &lt;head&gt;
      &lt;meta charset="UTF-8"&gt;
      &lt;title&gt;Swagger UI&lt;/title&gt;
      &lt;link rel="icon" type="image/png" href="images/favicon-32x32.png" sizes="32x32" /&gt;
      &lt;link rel="icon" type="image/png" href="images/favicon-16x16.png" sizes="16x16" /&gt;
      &lt;link href='./dist/css/typography.css' media='screen' rel='stylesheet' type='text/css'/&gt;
      &lt;link href='./dist/css/reset.css' media='screen' rel='stylesheet' type='text/css'/&gt;
      &lt;link href='./dist/css/screen.css' media='screen' rel='stylesheet' type='text/css'/&gt;
      &lt;link href='./dist/css/reset.css' media='print' rel='stylesheet' type='text/css'/&gt;
      &lt;link href='./dist/css/print.css' media='print' rel='stylesheet' type='text/css'/&gt;
      &lt;script src='./dist/lib/object-assign-pollyfill.js' type='text/javascript'&gt;&lt;/script&gt;
      &lt;script src='./dist/lib/jquery-1.8.0.min.js' type='text/javascript'&gt;&lt;/script&gt;
      &lt;script src='./dist/lib/jquery.slideto.min.js' type='text/javascript'&gt;&lt;/script&gt;
      &lt;script src='./dist/lib/jquery.wiggle.min.js' type='text/javascript'&gt;&lt;/script&gt;
      &lt;script src='./dist/lib/jquery.ba-bbq.min.js' type='text/javascript'&gt;&lt;/script&gt;
      &lt;script src='./dist/lib/handlebars-4.0.5.js' type='text/javascript'&gt;&lt;/script&gt;
      &lt;script src='./dist/lib/lodash.min.js' type='text/javascript'&gt;&lt;/script&gt;
      &lt;script src='./dist/lib/backbone-min.js' type='text/javascript'&gt;&lt;/script&gt;
      &lt;script src='./dist/swagger-ui.min.js' type='text/javascript'&gt;&lt;/script&gt;
      &lt;script src='./dist/lib/highlight.9.1.0.pack.js' type='text/javascript'&gt;&lt;/script&gt;
      &lt;script src='./dist/lib/highlight.9.1.0.pack.js' type='text/javascript'&gt;&lt;/script&gt;
      &lt;script src='./dist/lib/jsoneditor.min.js' type='text/javascript'&gt;&lt;/script&gt;
      &lt;script src='./dist/lib/marked.js' type='text/javascript'&gt;&lt;/script&gt;
      &lt;script src='./dist/lib/swagger-oauth.js' type='text/javascript'&gt;&lt;/script&gt;
  
      &lt;!-- Some basic translations --&gt;
      &lt;!-- &lt;script src='lang/translator.js' type='text/javascript'&gt;&lt;/script&gt; --&gt;
      &lt;!-- &lt;script src='lang/ru.js' type='text/javascript'&gt;&lt;/script&gt; --&gt;
      &lt;!-- &lt;script src='lang/en.js' type='text/javascript'&gt;&lt;/script&gt; --&gt;
  
      &lt;script type="text/javascript"&gt;
          $(function() {
              var springfox = {
                  "baseUrl": function() {
                      var urlMatches = /(.*)\/swagger-ui\/index.html.*/.exec(window.location.href);
                      return urlMatches[1];
                  },
                  "securityConfig": function(cb) {
                      $.getJSON(this.baseUrl() + "/swagger-resources/configuration/security", function(data) {
                          cb(data);
                      });
                  },
                  "uiConfig": function(cb) {
                                      alert(cb);
  
                      $.getJSON(this.baseUrl() + "/swagger-resources/configuration/ui", function(data) {
                          cb(data);
                      });
                  }
              };
              window.springfox = springfox;
              window.oAuthRedirectUrl = springfox.baseUrl() + './dist/o2c.html'
  
              window.springfox.uiConfig(function(data) {
                  window.swaggerUi = new SwaggerUi({
                      dom_id: "swagger-ui-container",
                      validatorUrl: data.validatorUrl,
                      supportedSubmitMethods: ['get', 'post', 'put', 'delete', 'patch'],
                      onComplete: function(swaggerApi, swaggerUi) {
                          initializeSpringfox();
                          if (window.SwaggerTranslator) {
                              window.SwaggerTranslator.translate();
                          }
                          $('pre code').each(function(i, e) {
                              hljs.highlightBlock(e)
                          });
                      },
                      onFailure: function(data) {
                          log("Unable to Load SwaggerUI");
                      },
                      docExpansion: "none",
                      apisSorter: "alpha",
                      showRequestHeaders: false
                  });
  
                  initializeBaseUrl();
  
                  $('#select_baseUrl').change(function() {
                      window.swaggerUi.headerView.trigger('update-swagger-ui', {
                          url: $('#select_baseUrl').val()
                      });
                      addApiKeyAuthorization();
                  });
  
                  function addApiKeyAuthorization() {
                      var authToken = JSON.parse(localStorage.getItem("jhi-authenticationtoken") || sessionStorage.getItem("jhi-authenticationtoken"));
                      var apiKeyAuth = new SwaggerClient.ApiKeyAuthorization("Authorization", "Bearer " + authToken, "header");
                      window.swaggerUi.api.clientAuthorizations.add("bearer", apiKeyAuth);
                  }
  
                  function getCSRF() {
                      var name = "XSRF-TOKEN=";
                      var ca = document.cookie.split(';');
                      for(var i=0; i&lt;ca.length; i++) {
                          var c = ca[i];
                          while (c.charAt(0)==' ') c = c.substring(1);
                          if (c.indexOf(name) !== -1) return c.substring(name.length,c.length);
                      }
                      return "";
                  }
  
                  function log() {
                      if ('console' in window) {
                          console.log.apply(console, arguments);
                      }
                  }
  
                  function oAuthIsDefined(security) {
                      return security.clientId
                      &amp;&amp; security.clientSecret
                      &amp;&amp; security.appName
                      &amp;&amp; security.realm;
                  }
  
                  function initializeSpringfox() {
                      var security = {};
                      window.springfox.securityConfig(function(data) {
                          security = data;
                          if (typeof initOAuth === "function" &amp;&amp; oAuthIsDefined(security)) {
                              initOAuth(security);
                          }
                      });
                  }
              });
  
              function maybePrefix(location, withRelativePath) {
                  var pat = /^https?:\/\//i;
                  if (pat.test(location)) {
                      return location;
                  }
                  return withRelativePath + location;
              }
  
              function initializeBaseUrl() {
                  var relativeLocation = springfox.baseUrl();
  
                  $('#input_baseUrl').hide();
  
                  $.getJSON(relativeLocation + "/swagger-resources", function(data) {
  
                      var $urlDropdown = $('#select_baseUrl');
                      $urlDropdown.empty();
                      $.each(data, function(i, resource) {
                          var option = $('&lt;option&gt;&lt;/option&gt;')
                          .attr("value", maybePrefix(resource.location, relativeLocation))
                          .text(resource.name + " (" + resource.location + ")");
                          $urlDropdown.append(option);
                      });
                      $urlDropdown.change();
                  });
  
              }
  
          });
      &lt;/script&gt;
  &lt;/head&gt;
  
  
  &lt;body class="swagger-section"&gt;
  &lt;div id='header'&gt;
      &lt;div class="swagger-ui-wrap"&gt;
          &lt;a id="logo" href="http://swagger.io"&gt;swagger&lt;/a&gt;
  
          &lt;form id='api_selector'&gt;
              &lt;div class='input'&gt;
                  &lt;select id="select_baseUrl" name="select_baseUrl"&gt;&lt;/select&gt;
              &lt;/div&gt;
              &lt;div class='input'&gt;&lt;input placeholder="http://example.com/api" id="input_baseUrl" name="baseUrl" type="text"/&gt;
              &lt;/div&gt;
          &lt;/form&gt;
      &lt;/div&gt;
  &lt;/div&gt;
  
  &lt;div id="message-bar" class="swagger-ui-wrap" data-sw-translate&gt;&amp;nbsp;&lt;/div&gt;
  &lt;div id="swagger-ui-container" class="swagger-ui-wrap"&gt;&lt;/div&gt;
  &lt;/body&gt;
  &lt;/html&gt;
  </code></pre>
  
  <p>here is docs.component.html</p>
  
  <pre><code>&lt;iframe src="swagger-ui/index.html" width="100%" height="900" seamless
      target="_top" title="Swagger UI" class="border-0"&gt;&lt;/iframe&gt;
  </code></pre>
  
  <p>here my server code is running perfectly @ localhost:6060. Butm localhost:6060/api/docs opening a blank page.</p>
  
  <p>here is the screen shot,</p>
  
  <p><a href="https://i.stack.imgur.com/VRVfW.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/VRVfW.png" alt="enter image description here"></a></p>
  
  <p>Please suggest me where i am doing wrong.</p>