ngmilk logo
Published on

Pretty URLs or HTML5 mode for any front-end SPA: Angular, React & more


The original pretty URLs tutorial has helped quite a few since its inception. Fast-forward a few years and things haven't changed much. Yes, search engines are getting better at indexing dynamic content, but this didn't remove the need for pretty URLs.

We should see the hashbang #! for what it is: a dirty little hack. So let's get rid of it!

Without further ado, here's how to get pretty URLs on some of the most popular server setups & front-end frameworks.

Apache .htaccess

RewriteEngine On
# If an existing asset or directory is requested go to it as it is
RewriteRule ^ - [L]

# If the requested resource doesn't exist, use index.html
RewriteRule ^ /index.html

nginx /etc/nginx/sites-available/default*

server {
  root /var/www/mysite/; # or location of your choice
  index index.html;

  location /{
    try_files $uri $uri/ =404;
  • Make sure to create a config under sites-available & symlink it to sites-enabled i.e.: sudo ln -s /etc/nginx/sites-available/default /etc/nginx/sites-enabled/default

IIS web.config**

<?xml version="1.0" encoding="UTF-8"?>
        <rule name="Handle History Mode and custom 404/500" stopProcessing="true">
          <match url="(.*)" />
          <conditions logicalGrouping="MatchAll">
            <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
            <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
          <action type="Rewrite" url="/" />

**Make sure to install URL Rewrite for this to work.

You should be pretty much good to go. Next, make sure your framework / router has HTML5 or pushState enabled & is set up correctly. If you are not sure, see the section below.

Enabling pushState / HTML5 mode in different frameworks & routers

Angular 6

Not much work required, pushState is enabled by default. In index.html, be sure to include a base href, like so:

<base href="/">


React / react-router v4

Use <BrowserRouter>[link] instead of <HashRouter>[link] and you're good.

Polymer / app-route v2

Use <app-route> and it should have pushState on by default.

Vue.js / vue-router v3

You need to set the router mode to 'history'.

const router = new VueRouter({
  mode: 'history',
  routes: [...]

See more on