htaccess redirect for Angular routes - angularjs

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?

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.

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

A poor man's solution (not using mod_rewrite):
ErrorDocument 404 /index.html

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

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>

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.

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]

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).

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

Related

AngularJS w/Prerender 404 error on home page

All of my angularjs site works with prerender except for the home page. When crawled, it sends back a 404 page. I have reason to believe it is this line of code in my .htaccess file, RewriteRule ^(.*)$ http://service.prerender.io/https://%{HTTP_HOST}/$1 [P,L] but I am not sure.
<IfModule mod_rewrite.c>
RewriteEngine On
# If requested resource exists as a file or directory
# (REQUEST_FILENAME is only relative in virtualhost context, so not usable)
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f [OR]
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d
# Go to it as is
RewriteRule ^ - [L]
# If non existent
# If path ends with / and is not just a single /, redirect to without the trailing /
RewriteCond %{REQUEST_URI} ^.*/$
RewriteCond %{REQUEST_URI} !^/$
RewriteRule ^(.*)/$ $1 [R,QSA,L]
# Handle Prerender.io
RequestHeader set X-Prerender-Token "notprovidingthiscode"
RewriteCond %{HTTP_USER_AGENT} baiduspider|facebookexternalhit|twitterbot|rogerbot|linkedinbot|embedly|quora\ link\ preview|showyoubot|outbrain|pinterest [NC,OR]
RewriteCond %{QUERY_STRING} _escaped_fragment_
# Proxy the request
RewriteRule ^(.*)$ http://service.prerender.io/https://%{HTTP_HOST}/$1 [P,L]
# If non existent
# Accept everything on index.html
RewriteRule ^ /index.html
The issue turned out to be that the .htaccess file was serving example.com/index.html rather than just example.com when accessing the root of the angularjs app. That in turn didn't play well with ui-router because the $stateProvider doesn't serve filenames at the end of urls without being explicit. Accessing example.com/index.html did indeed cause my page to throw a 404 error $urlRouterProvider.otherwise('404');
Adding the following code fixed my issue.
$urlRouterProvider.when('/index.html', '/');
This redirects example.com/index.html to example.com which points to the correct rendering in prerender.io.

Configuring htaccess file for React Router on Apache Server

I have deployed a React app with React Router to my Bluehost server, and need to configure the htaccess file to redirect all of my routed URLs (/portfolio, /about, etc) to index.html instead of trying to fetch a new file from the server and throwing a 404.
I have read about countless similar problems in which the solution seems to be to add this into your htaccess file:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-l
RewriteRule . /index.html [L]
</IfModule>
I tried this, but I am still getting 404's when I try to visit any page of my site directly that isn't the homepage. I'm wondering if there is anything else in my existing htaccess file that is preventing the above code from working?
There was some code already in there from Bluehost, and I see another IfModule statement, so I'm wondering if that one is overwriting the first one. However I am afraid to edit it and break something, as it clearly says "do not edit." Here is my full htaccess code:
Header always set Content-Security-Policy: upgrade-insecure-requests
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-l
RewriteRule . /index.html [L]
</IfModule>
# php -- BEGIN cPanel-generated handler, do not edit
# Set the “ea-php74” package as the default “PHP” programming language.
<IfModule mime_module>
AddHandler application/x-httpd-ea-php74 .php .php7 .phtml
</IfModule>
# php -- END cPanel-generated handler, do not edit
# BEGIN WordPress
# The directives (lines) between "BEGIN WordPress" and "END WordPress" are
# dynamically generated, and should only be modified via WordPress filters.
# Any changes to the directives between these markers will be overwritten.
# END WordPress
Any ideas? I've double-checked that my BrowserRouter is set up correctly and also tried a few other htaccess configurations. I want to avoid using HashRouter or Node if possible but am getting frustrated. I can provide my React code as well if needed, but I'm pretty sure the error is not with the React setup.
You can create a virtual host file in the /etc/apache/sites-available folder and add this:
<VirtualHost *:8080>
ServerName example.com
DocumentRoot /var/www/httpd/example.com
<Directory "/var/www/httpd/example.com">
...
RewriteEngine on
# Don't rewrite files or directories
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
# Rewrite everything else to index.html to allow html5 state links
RewriteRule ^ index.html [L]
</Directory>
</VirtualHost>
This worked for me

Angular with Prerender (.htaccess settings)

I am trying to set up Angular 1.5 app for server side rendering for the crawlers by using Prerender service.
And everything works fine for the inner pages but there is a problem with the main page's rendering - the crawler sees the 404 page instead of the main page.
I suppose there is a problem with some other rules in my .htaccess - except the rules for the Prerender, I use two other rules for all the pages:
rewriting urls without trailing slashes onto the urls with trailing slashes
rewriting urls with www on the urls without www
Will be appreciate for any tips!
Here is my .htaccess file for Apache serveer
RequestHeader set X-Prerender-Token "MyToken"
RewriteEngine On
RewriteCond %{HTTP_HOST} ^www.example.com$ [NC]
RewriteRule ^(.*)$ http://example.com/$1 [R=301,L]
# 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]
RewriteCond %{REQUEST_URI} ^/$
RewriteCond %{QUERY_STRING} ^_escaped_fragment_=/?(.*)$
RewriteRule ^(.*)$ /snapshots/%1? [NC,L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*[^/])$ /$1/ [L,R=301]
<IfModule mod_proxy_http.c>
RewriteCond %{HTTP_USER_AGENT} Googlebot|bingbot|Googlebot-Mobile|Baiduspider|Yahoo|YahooSeeker|DoCoMo|Twitterbot|TweetmemeBot|Twikle|Netseer|Daumoa|SeznamBot|Ezooms|MSNBot|Exabot|MJ12bot|sogou\sspider|YandexBot|bitlybot|ia_archiver|proximic|spbot|ChangeDetection|NaverBot|MetaJobBot|magpie-crawler|Genieo\sWeb\sfilter|Qualidator.com\sBot|Woko|Vagabondo|360Spider|ExB\sLanguage\sCrawler|AddThis.com|aiHitBot|Spinn3r|BingPreview|GrapeshotCrawler|CareerBot|ZumBot|ShopWiki|bixocrawler|uMBot|sistrix|linkdexbot|AhrefsBot|archive.org_bot|SeoCheckBot|TurnitinBot|VoilaBot|SearchmetricsBot|Butterfly|Yahoo!|Plukkie|yacybot|trendictionbot|UASlinkChecker|Blekkobot|Wotbox|YioopBot|meanpathbot|TinEye|LuminateBot|FyberSpider|Infohelfer|linkdex.com|Curious\sGeorge|Fetch-Guess|ichiro|MojeekBot|SBSearch|WebThumbnail|socialbm_bot|SemrushBot|Vedma|alexa\ssite\saudit|SEOkicks-Robot|Browsershots|BLEXBot|woriobot|AMZNKAssocBot|Speedy|oBot|HostTracker|OpenWebSpider|WBSearchBot|FacebookExternalHit [NC,OR]
RewriteCond %{QUERY_STRING} _escaped_fragment_
# Only proxy the request to Prerender if it's a request for HTML
RewriteRule ^(?!.*?(\.js|\.css|\.xml|\.less|\.png|\.jpg|\.jpeg|\.gif|\.pdf|\.doc|\.txt|\.ico|\.rss|\.zip|\.mp3|\.rar|\.exe|\.wmv|\.doc|\.avi|\.ppt|\.mpg|\.mpeg|\.tif|\.wav|\.mov|\.psd|\.ai|\.xls|\.mp4|\.m4a|\.swf|\.dat|\.dmg|\.iso|\.flv|\.m4v|\.torrent|\.ttf|\.woff))(.*) http://service.prerender.io/http://example.com/$2 [P,L]
</IfModule>
# If the requested resource doesn't exist, use index.html
RewriteRule ^ /index.html
You have this section:
RewriteCond %{REQUEST_URI} ^/$
RewriteCond %{QUERY_STRING} ^_escaped_fragment_=/?(.*)$
RewriteRule ^(.*)$ /snapshots/%1? [NC,L]
Which will try to serve files from your /snapshots/ directory if _escaped_fragment_ is in the URL. That doesn't have anything to do with Prerender.io so you'll probably want to remove that section, as it could be the cause of the 404.
You're also checking Googlebot and Bingbot by their user agents which is a bad idea because they could penalize you for cloaking.

Configuring .htaccess file to serve angular and api routes

I am lost with the configuration of my .htaccess file. I have a website serving the client part (angular.js app) and api routes, which are handled with the slim framework.
My html pages are served with the ui-router, but in order to handle the page refresh, I have to do an url rewrite.
That's my file so far :
DirectoryIndex index.html index.php
#
# Redirect all to index.php
#
RewriteEngine On
# if a directory or a file exists, use it directly (static assets)
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
#if route starts with "api", route to index.php (slim framework)
RewriteRule ^api/ index.php [L]
#else (it means we are serving the angular routes)
RewriteRule ^(.*) /index.html [NC,L]
It's, of course, not working. The real problem is that I don't know if my logic is good at first (or I am far far far far away...), and then how to write it (I am new to apache syntax and I don't really understand everything).
With 2 virtual hosts
API htaccess:
RewriteEngine On
# Redirect Trailing Slashes
RewriteRule ^(.*)/$ /$1 [L]
# Redirect Root
RewriteRule ^$ api.php [L]
# Handle API Requests
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ api.php [L]
Front virtual host:
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 (html5mode)
RewriteRule ^ /index.html
# Redirect /api to API virtual host
<Location /api>
ProxyPass http://127.0.0.1:8090 # Change here to the API virtual host binding
</Location>
The front virtual host will serve the requested file if it exists (except for /api/*), or will serve index.html if it does not. All /api requests will be forwarded to API virtual host. The API virtual host will serve any existing file, or will rewrite to api.php, which will handle your API calls.
With 1 virtual host
<IfModule mod_negotiation.c>
Options -MultiViews
</IfModule>
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]
RewriteCond %{REQUEST_URI} ^/api/
RewriteRule ^ api.php [L]
# If the requested resource doesn't exist, use index.html (html5mode)
RewriteRule ^ /index.html [L]
Same behavior as above
WARNING: trying to include a non-existant file into the angular app may produce an infinite loading loop (index.html will load, then it will load the non-existant file, which will be index.html due to html5mode, etc...)

Angular app in subfolder Laravel + htaccess

I have a laravel 5 installation. Works perfect.
.htaccess inside the public folder is default and works fine:
<IfModule mod_rewrite.c>
<IfModule mod_negotiation.c>
Options -MultiViews
</IfModule>
RewriteEngine On
# Redirect Trailing Slashes...
RewriteRule ^(.*)/$ /$1 [L,R=301]
# Handle Front Controller...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]
Inside the public (root) folder i have a folder named webapp
webapp includes a web version of my app build with angularjs and it needs push states
the ideal .htaccess inside the webapp folder would be:
<ifModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
# html5 pushstate (history) support:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !index
RewriteRule (.*) index.html [L]
</ifModule>
But it seems the .htaccess inside the webapp folder is not triggered.
I probably need to write the conditions and rules inside the laravel public folder. But i'm not sure how to handle this and in what order etc.
Can someone help me to combine the 2 .htaccess files into one to place inside the public folder.
This works for me:
bash-3.2$ cat .htaccess
<IfModule mod_rewrite.c>
<IfModule mod_negotiation.c>
Options -MultiViews
</IfModule>
RewriteEngine On
# Redirect Trailing Slashes...
RewriteRule ^(.*)/$ /$1 [L,R=301] #This creates a redirect loop for angular
# IF the resource does not exist as an actual directory...
RewriteCond %{REQUEST_FILENAME} !-d
# OR an actual file...
RewriteCond %{REQUEST_FILENAME} !-f
# AND is being requested from angular directory a/
# THEN send it to the angular routing file
RewriteRule ^(a/.*) /a/index.html [NC,L]
# IF the resource does not exist as an actual directory...
RewriteCond %{REQUEST_FILENAME} !-d
# OR an actual file...
RewriteCond %{REQUEST_FILENAME} !-f
# THEN send the traffic to laravel routing
RewriteRule ^ index.php [L]
</IfModule>
bash-3.2$

Resources