89

I have an angular application with several routes, such as:

site.com/
site.com/page
site.com/page/4

Using angular's html5 routing mode, these resolve correctly when you click links to them from within the application, but of course are 404 errors when you do a hard refresh. To fix this, I've tried implementing a basic htaccess rewrite.

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_METHOD} !OPTIONS
RewriteRule ^(.*)$ index.html [L]

This works for the angular requests, however when I try to load scripts or make ajax calls within my domain, such as:

<script src="/app/programs/script.js"></script>

This script doesn't load - it's request is redirected and it tries to load the index.html page as the .htaccess thinks it should reroute the request - not knowing that this file does exist and it should load the file instead of redirect.

Is there any way I can have the htaccess redirect the request to index.html (with the view parameters) only if there is not an actual file that it should resolve to?

Lucas Penney
  • 2,624
  • 4
  • 27
  • 36

11 Answers11

177

Use a snippet like:

RewriteEngine on
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^.*$ - [NC,L]

RewriteRule ^(.*) /index.html [NC,L]

This will skip to the actual resource if there is one, and to index.html for all AngularJS routes.

Omar Einea
  • 2,478
  • 7
  • 23
  • 35
rajasaur
  • 5,340
  • 2
  • 27
  • 22
  • 1
    This is almost perfect - it doesn't seem to handle routes with parameters, for example site.com/page/4, though I'm not sure why. – Lucas Penney Mar 30 '14 at 07:10
  • Can you show how your routes are configured or maybe check redirect log after adding a "RewriteLogLevel 9 RewriteLog /tmp/rewrite.log" to apache to see what is getting routed. – rajasaur Mar 30 '14 at 09:49
  • 1
    Turns out this worked fine - I just had some file references without a leading slash (/) and so were trying to resolve relatively in routes with parameters. – Lucas Penney Mar 30 '14 at 18:10
  • 2
    @rajasaur thank you very much - this is why I love stackoverflow. Helped me quickly solve what I thought I would be up all night trying to figure out. – jamie Jun 07 '15 at 01:43
  • @rajasaur I am working with grunt to load and configure my front end files. I am not able to route my angular requests to laravel as api's.I hav e posted my question here -> http://stackoverflow.com/questions/31698217/running-laravel-app-using-grunt-fails-to-start-session-on-local-environment How could I route my api routes and manage sessions? – KillABug Jul 30 '15 at 09:35
  • 2
    Where and how do you put this htaccess file?? – WJA Oct 15 '15 at 23:29
  • @JohnAndrews: Depends on how you configured apache, its just a set of rewrite rules, so it can go in any of VirtualHost, .htaccess or Core configuration – rajasaur Oct 16 '15 at 02:22
  • how to do the same thing in wildfly server – atul Sep 05 '16 at 15:04
  • @atul, I would have a fronting server like apache or nginx and handle the static resources and routing over there. Only the proxied or backend requests will come to wildfly, everything else is handled by a frontend server. – rajasaur Sep 07 '16 at 03:33
  • 1
    Hi, In my case I have `http://www.domain.com/dashboard`. So the url has `/dashboard` then only I want to open the angular project. Please help me how to write `.htaccess` for the same. Actually I have 2 different angular project. 1st one will render on `http://www.domain.com` and 2nd one should run on `http://www.domain.com/dashboard`. – Anil Kumar Pandey Oct 28 '16 at 20:26
  • I am using grunt browserSync to serv up my content locally... Is there a version of this solution that will work for me? the `php -S localhost:port` will work for reloading ng-views but not the browserSync server. – Omar Jan 25 '17 at 19:08
  • I just wanted to mention that while the most common implementations of this type of script did not handle anchor scroll in my application i.e:`src="/foo#bar"`, this one does. – jonc.js Apr 22 '17 at 07:43
  • @rajasaur This doesn't seem to handle the routes with parameters. My router looks like this ; export const routes: Routes = [ { path: 'activate/:activation_token', component: ActivationComponent }, { path: 'reset-password/:reset_token', component: ResetPasswordComponent }, { path: '**', component: NotFoundComponent }, ]; export const routing: ModuleWithProviders = RouterModule.forRoot(routes, { preloadingStrategy: PreloadAllModules, // useHash: true }); – kplus Nov 24 '17 at 16:45
  • can use RewriteRule ^(.*)/myapp/index.html [NC,L] for virtual hosts. where myapp will be in var/www/html folder – Shatayu Darbhe Jun 03 '18 at 06:14
  • The "/index.html" in the last line needs to be preceded by an '.' to make the solution work in subfolders. Additionally "RewriteBase /YOURSUBFOLDER" needs to be added – Awsed Jul 26 '18 at 12:24
  • In my usecase, I added index.html to the directory line: `RewriteCond %{REQUEST_FILENAME}/index.html -d` to ignore directories WITHOUT an index.html file to redirect to that route – Jacob Morrison Mar 27 '20 at 12:53
  • @rajasaur perfect and work with parameter url like slug – Bachtiar Panjaitan Oct 05 '20 at 09:46
  • not working when has more than 1 path ... – Alberto Acuña Feb 11 '22 at 13:20
23

There is a problem if app requested directive template file, but file is missing. In some case it caused app requested multiple script file in the index.html.

we should send 404 response instead of index.html file if file does not exist. So i add simple regex pattern to identify missing file request.

RewriteEngine On
# If an existing asset or directory is requested go to it as it is
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f [OR]
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d
RewriteRule ^ - [L]

# If the requested pattern is file and file doesn't exist, send 404
RewriteCond %{REQUEST_URI} ^(\/[a-z_\-\s0-9\.]+)+\.[a-zA-Z]{2,4}$
RewriteRule ^ - [L,R=404]

# otherwise use history router
RewriteRule ^ /index.html
port115
  • 411
  • 4
  • 9
  • if the file does not exist instead if doing `RewriteRule ^ - [L,R=404]` can I redirect to index.html using `RewriteRule ^ /index.html` or `RewriteRule ^ - [L,R=/index.html]` ? I am in a appserver environment and the paths are breaking quite a lot. Not used to such changes in .htaccess but just want to redirect 404 to index.html as well. – Gary Oct 10 '16 at 07:06
  • 1
    Yes you can skip the 404 part, but what's the point. This `.htaccess` approach is intended for missing asset / template file on angular directives, not missing path. If you want to handling missing path, you should configure router provider itself, or you can use router events. – port115 Oct 11 '16 at 09:41
  • I do not want a 404 handle for the .htaccess . I want to handle all that with index.html angular file – Gary Oct 11 '16 at 12:34
  • +1 for giving the only answer that uses `%{DOCUMENT_ROOT}` in the RewriteCond which is required when you are within a VirtualHost - otherwise the condition will never be true! – not2savvy May 24 '17 at 10:14
9

A poor man's solution (not using mod_rewrite):

ErrorDocument 404 /index.html
Cito
  • 5,365
  • 28
  • 30
9

In my case i create .htaccess file like below

RewriteEngine on
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^.*$ - [NC,L]

RewriteRule ^(.*) ./index.html [NC,L]

just added . before /index.html and add that file in my domain like https://example.com/subfolder and it's works fine

Sachin from Pune
  • 656
  • 8
  • 19
5

Check out this link : https://stackoverflow.com/a/49455101/5899936 Create .htaccess file in root folder and pase this in the .htaccess

 <IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteBase /
  RewriteRule ^index\.html$ - [L]
  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteCond %{REQUEST_FILENAME} !-d
  RewriteRule . /index.html [L]
</IfModule>
Sumit Jaiswal
  • 787
  • 8
  • 11
  • Thanks it worked for me. If it is sub directory, just add `RewriteBase /SubDir` instead of `RewriteBase /` – sree Feb 06 '19 at 01:33
2

I will provide another detailed htaccess file in case you need to :

  • Considerate the baseUrl and default index file
  • Remove the leading slash to manage :params

Here is my htaccess, very close, but more specific :

DirectoryIndex index.html
RewriteEngine on
RewriteBase /subDir
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^.*$ - [NC,L]
RewriteRule ^(.*) index.html [NC,L]
andrea06590
  • 1,259
  • 3
  • 10
  • 22
2

There is a great "Angular .htaccess generator" available on julianpoemp.github.io/ngx-htaccess-generator/.

The generator is open-source (the source code can be found on github.com/julianpoemp/ngx-htaccess-generator).

The generated .htaccess looks like this by default and works fine for me:

# Generated with ngx-htaccess-generator v1.2.0
# Check for updates: https://julianpoemp.github.io/ngx-htaccess-generator/
#
# Transparency notice: Some parts were extracted from
# Apache Server Configs v5.0.0 | MIT License
# https://github.com/h5bp/server-configs-apache
# Extracted parts are wrapped by "START Extract from ASC"

<IfModule mod_rewrite.c>
  RewriteEngine On
  
  # Redirection of requests to index.html
  RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f [OR]
  RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d
  RewriteRule ^.*$ - [NC,L]
  # Redirect all non-file routes to index.html
  RewriteRule ^(?!.*\.).*$ index.html [NC,L]
</IfModule>

Successfully tested with Angular 13.0.3.

David Wolf
  • 1,400
  • 1
  • 9
  • 18
  • 1
    I'm glad that the generator is helpful to you :) If you have any criticism or suggestion for improvement feel free to create an issue on the repository page :) – julianpoemp Jan 12 '22 at 15:43
1

In my case, we needed a more feature reach .htaccess configuration, like with:

  • Forcing to HTTPS protocol (commented out in below).
  • Translation of Authorization header to HTTP_AUTHORIZATION environment variable.
  • Cross-Origin Resource Sharing (CORS).
  • And disabled caching for specific formats (commented out as well).

# If mod_rewrite is not present.
<IfModule !mod_rewrite.c>
  FallbackResource /index.html
</IfModule>

<IfModule mod_rewrite.c>
  RewriteEngine On
  # Prefix for all rewritten routes ("index.html" gets "/index.html").
  RewriteBase /

  # Redirects to HTTPS protocol (once uncommented).
  #
#  RewriteCond %{HTTPS} !on
#  RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [L]

  # Make sure Authorization HTTP header is available.
  RewriteRule ^ - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]

  # Allows access to existing files or dirs.
  RewriteCond %{REQUEST_FILENAME} -s [OR]
  RewriteCond %{REQUEST_FILENAME} -l [OR]
  RewriteCond %{REQUEST_FILENAME} -d
  RewriteRule ^.*$ - [NC,L]

  # Prevents treating the main-script as a route.
  RewriteRule ^index\.html$ - [L]
  # Redirect anything else to main-script
  RewriteRule ^(.*) index.html [NC,L]
</IfModule>

# Enable Cross-Origin Resource Sharing (CORS)
#
<IfModule mod_headers.c>
  Header merge Vary Origin

  # Allows any origin (just like "*", but works in more cases)
  SetEnvIf Origin "^(http(s)?://[^/:]*(?::\d{1,5})?)?" REQUEST_ORIGIN=$1
  Header always append Access-Control-Allow-Origin %{REQUEST_ORIGIN}e env=REQUEST_ORIGIN

  Header always set Access-Control-Allow-Credentials "true"
  Header always set Access-Control-Allow-Methods "GET, POST, PUT, DELETE"
  Header always set Access-Control-Allow-Headers "*"
  Header always set Access-Control-Expose-Headers "*"
</IfModule>

# Disables Browser caching for production (edit pattern as you wish).
#
#<FilesMatch "\.(html|htm|js|json|css)$">
#   # Ensures "Expires" header is not overridden by module.
#   <IfModule mod_expires.c>
#       ExpiresActive Off
#   </IfModule>
#   <IfModule mod_headers.c>
#       FileETag None
#       Header unset ETag
#       Header unset Pragma
#       Header unset Cache-Control
#       Header unset Last-Modified
#       Header set Pragma "no-cache"
#       Header set Cache-Control "max-age=0, no-cache, no-store, must-revalidate"
#       Header set Expires "Mon, 10 Apr 1972 00:00:00 GMT"
#   </IfModule>
#</FilesMatch>

Note: We use [L] instead of [L,R=301] as the latter causes Browser to cache redirects permanently (and even if someday that route is a file it will still get redirected).

Top-Master
  • 7,611
  • 5
  • 39
  • 71
0

Since angular is an AJAX application, html4 cannot implement it. All you have to do is activate the html5 mode in the .htaccess file.

angular.module('main', []).config(['$locationProvider', function($locationProvider) {
  ...
  $locationProvider.html5Mode(true);
  ...
});

RewriteEngine On
# If an existing asset or directory is requested go to it as it is
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f [OR]
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d
RewriteRule ^ - [L]

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

The use this snippet still works. I am using cpanel for a site I created for a customer using Angular 13. I was getting the 404 error when I refreshed the page and got the same 404 error when I tried to navigate to a different page from the browser. I'm not sure when the ,htaccess code was generated on the hosting site or if it was with a tool or whether it was added when the SSL was created for this site. I added the snippet above to what was already there and it worked!

 <IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteCond %{HTTPS} off
  RewriteCond %{HTTP_HOST} ^(www\.)?domain name\.com$ [NC]
  RewriteRule ^(.*)$ https://domain name.com/$1 [L,R=301]


  #Redirection of requests to index.html
  RewriteCond %{REQUEST_FILENAME} -s [OR]
  RewriteCond %{REQUEST_FILENAME} -l [OR]
  RewriteCond %{REQUEST_FILENAME} -d
  RewriteRule ^.*$ - [NC,L]
  RewriteRule ^(.*) /index.html [NC,L]
user2280852
  • 135
  • 11
0

For Angular 2+, the following .htaccess file works.

Here is the .htaccess content:

<IfModule mod_rewrite.c>
  RewriteEngine On
  
  # Redirection of requests to index.html
  RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f [OR]
  RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d
  RewriteRule ^.*$ - [NC,L]
  # Redirect all non-file routes to index.html
  RewriteRule ^(?!.*\.).*$ index.html [NC,L]
</IfModule>

If you have not created the .htacess file yet, create it in /public_html directory.

MB_18
  • 1,620
  • 23
  • 37