htaccess redirect for Angular routes
Angularjs.HtaccessRedirectWeb ApplicationsRoutingAngularjs Problem Overview
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?
Angularjs Solutions
Solution 1 - Angularjs
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.
Solution 2 - Angularjs
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
Solution 3 - Angularjs
A poor man's solution (not using mod_rewrite):
ErrorDocument 404 /index.html
Solution 4 - Angularjs
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
Solution 5 - Angularjs
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>
Solution 6 - Angularjs
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]
Solution 7 - Angularjs
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
.
Solution 8 - Angularjs
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 toHTTP_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)$">
# <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).