Mastodon

Angular 6 on Cloud Foundry with URLs without Hash

This article is about deploying an Angular 6 application to Pivotal Cloud Foundry so that URLs without a hash can be used.

First, some Angular background. Angular is a SPA-framework which means that an Angular app has exactly one html-file: the index.html. This is the only file a web server knows, that’s why theoretically there is only one URL: the one pointing to index.html. However in Angular, it’s possible to create routes like mydomain.com/sub. Because there is no sub.html file, a request directly to this file will result in a 404 error because the web server cannot serve a non-existing file. To allow defining and visiting those paths, Angular has a build-in solution. The RouterModule, which is responsible for creating routes like the before mentioned /sub, can be configured to insert hashes in each URL:

RouterModule.forRoot(routes, { useHash: true })

Those hashes separate the known URL to the existing index.html and the additional path, for example in “mydomain.com/#/sub”. The web server will forward the request to the index.html which will deliver the requested and dynamically created Angular-content.

However, using hashes in URLs is not pretty, so I wanted to get rid of them.

Approach 1: Using staticfile_buildpack with modified nginx.conf

Setting up a new Angular application can be done quickly by using the staticfile-buildpack which simply serves the Angular application without much configuration. Here’s the manifest.yml for this simple case:

---
applications:
- name: ithubbs_test
  instances: 1
  buildpack: staticfile_buildpack

The first approach to removing the hashes is to remove the useHash-parameter in the application source code:

RouterModule.forRoot(routes)

This will lead to clean URLs that work when accessed from “within” the Angular application: If you call the base URL mydomain.com which resolves to the index.html and navigate from there to other paths like mydomain.com/sub, everything will work. However, direct requests to this path will result in a 404 error. This is also true for refreshing an opened Angular application with F5.

The nginx-server, which is the web server used in Pivotal Cloud Foundry, can be configured to forward every request to index.html and thereby to Angular. Copying the nginx.conf-file from a preciously successfully deployed and running server is a good way of getting a definitively working config file that can be modified. Another way of getting this file is via Github. This is the modification to forward every request to index.html:

server {
	location / {
	# Configure nginx to forward URLs like mydomain.com/topic1/detail to index.html so that request can be handled by Angular instead of nginx trying to find an topic1/detail.html.
	# Default config:
	# index index.html index.htm Default.htm;
	try_files $uri $uri/ /index.html;
	}
}

Simply changing nginx.conf on a deployed server is not a good solution because the container gets rebuild with every new deployment. Hence, putting the modified version in the local dist-folder (next to index.html) will upload it with the next deployment. Simply adding the try_files-configuration and using the staticfile_buildpack will solve the URL-hash-problem, but at a cost. Starting the server will bring the following warning:

-----> Configuring nginx
  **WARNING** overriding nginx.conf is deprecated and highly discouraged, as it breaks the functionality of the Staticfile and Staticfile.auth configuration directives. Please use the NGINX buildpack available at: https://github.com/cloudfoundry/nginx-build

Approach 2: Using nginx-buildpack with modified nginx.conf

To use the right buildpack, it has to be referenced in the manifest.yml:

---
applications:
- name: ithubbs_test
  instances: 1
  buildpack: https://github.com/cloudfoundry/nginx-buildpack.git

Also, the structure of the dist-folder has to be as follows:

dist
|--- public
     |   index.html
     |   favicon.ico
     |   main.xxxxx.js
     |   runtime.ecxxxxx.js
     |   styles.xxxxx.css
|    mime.types
|    nginx.conf

The public-folder is where all the Angular-generated files will be. mime.types can be copied from the nginx-buildpack Github account, as well as a simple version of nginx.conf. It is very important to use in the nginx.conf.

This setup will lead to clean, hash-free URLs as well as no warnings when deploying.