Angular 6+ deploy to Apache server by solving 404 Not found error on page refresh
This article will hep you to deploy angular 6+ application on apache server also to solve 404 not found error on page refresh.
Getting Started
For now let's hope you have already setup a new Angular6+ project or has an existing Angular6+ project.Here we are considering working with an existing Angular6+ App. I have named my project as angular-deployemnt-sample.
The project was scaffolded using Angular cli which can be used to generate basic app structure for your projects.
After generating the basic app using Cli
and setting up basic routing module and a home component the project structure looks something like this
if you want to run the same in local just use the command
ng serve --open
The above command will start serving the project at the url
http://localhost:4200/
Build app for production
Before deploying to the apache server we need to build the project and optimise it for production.
ng build --prod
Setting our Base
To link around your application using relative links, you will need to set a <base>
in the <head>
of your document. while setting base you will have to consider where the projects resides in the server
In angular 6+ cli allows you to set the base automatically while building the project.if your project files is placed in the /var/www/html
folder (that is project files copied from dist folder after ng build --prod to root folder of your server) or you are running on ng serve (while under development in local) then your base should be
<base href="/">
if your project files resides in some folder somefolder then base should be
<base href="/somefolder/">
To set the base during build to root folder /var/www/html
you can use the regular command
ng build --prod
To set the base during build when your project files resides in some folder somefolder then use
ng build --prod --base-href somefolder
Now after you copying the files from your dist
folder to apache servers root folder /var/www/html
the project will work until you refresh the page
404 Not found error on page refresh
Now that you have deployed the project to server, and everything seems ok until you try to refresh the page . You will find that your app throws a 404 Not found error
Solution
To solve this there are two approaches or strategy
We will use PathLocationStrategy
The default strategy used in Angular is the PathLocationStrategy
so we need to do nothing to enable it. And this will be the statergy that we are going to use here
It takes advantage of a relatively new HTML5 API called pushstate (from the HTML5 history API).
By using pushstate we can change the URL
and not have the browser request the page from the server and without needing to use a hash fragment.
Unfortunately it has one big downside, if we then reloaded the page or bookmarked and opened it later the browser would make a request to the server
By using a hash fragment the server never needs to know about any application URL
, it will only ever get asked for the root page and it will only ever return the root page.
But by using a PathLocationStrategy
the server needs to be able to return the main application code for every URL
, not just the root URL
.
So with PathLocationStrategy
we need to co-operate with a server side that supports this functionality, it’s possible and quite easy to implement a server side like this but it does require some effort and cooperation.
When you have html5Mode
enabled, the #
character will no longer be used in your URLs
. The #
symbol is useful because it requires no server side configuration. Without #
, the URL
looks much nicer, but it also requires server side rewrites.
Configuring Apache Server
we need to configure the server for rewrites, and this involve following steps
- Activate
mod_rewrite
- Edit Apache configuration file
- Restart Apache
- Setting Up
.htaccess
First we need to activate mod_rewrite
. It's available but not enabled with a clean Apache 2
installation
You can use the following command
sudo a2enmod rewrite
This will activate the module or alert you that the module is already enabled.
By default, Apache prohibits using an .htaccess
file to apply rewrite rules, so first you need to allow changes to the file. Open the default Apache configuration file using nano or your favourite text editor.
sudo nano /etc/apache2/sites-available/000-default.conf
Inside that file, you will find a <VirtualHost *:80>
block starting on the first line. Inside of that block, add the following new block so your configuration file looks like the following. Make sure that all blocks are properly indented.
<VirtualHost *:80>
<Directory /var/www/html>
Options Indexes FollowSymLinks MultiViews
AllowOverride All
Require all granted
</Directory>
</VirtualHost>
Now Save and close the file .To put these changes into effect, restart Apache
sudo systemctl restart apache2
mod_rewrite
is now fully enabled. In the next step we will set up an .htaccess
file in the root folder /var/www/html
where our angular files are placed and we we'll use it to define rewrite rules for redirects
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]
Also since we need to increase SEO points, i like to add some more code to .htaccess to enable compression
and leverage browser Caching
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]
# Enable Compression
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE application/javascript
AddOutputFilterByType DEFLATE application/rss+xml
AddOutputFilterByType DEFLATE application/vnd.ms-fontobject
AddOutputFilterByType DEFLATE application/x-font
AddOutputFilterByType DEFLATE application/x-font-opentype
AddOutputFilterByType DEFLATE application/x-font-otf
AddOutputFilterByType DEFLATE application/x-font-truetype
AddOutputFilterByType DEFLATE application/x-font-ttf
AddOutputFilterByType DEFLATE application/x-javascript
AddOutputFilterByType DEFLATE application/xhtml+xml
AddOutputFilterByType DEFLATE application/xml
AddOutputFilterByType DEFLATE font/opentype
AddOutputFilterByType DEFLATE font/otf
AddOutputFilterByType DEFLATE font/ttf
AddOutputFilterByType DEFLATE image/svg+xml
AddOutputFilterByType DEFLATE image/x-icon
AddOutputFilterByType DEFLATE text/css
AddOutputFilterByType DEFLATE text/html
AddOutputFilterByType DEFLATE text/javascript
AddOutputFilterByType DEFLATE text/plain
</IfModule>
<IfModule mod_gzip.c>
mod_gzip_on Yes
mod_gzip_dechunk Yes
mod_gzip_item_include file .(html?|txt|css|js|php|pl)$
mod_gzip_item_include handler ^cgi-script$
mod_gzip_item_include mime ^text/.*
mod_gzip_item_include mime ^application/x-javascript.*
mod_gzip_item_exclude mime ^image/.*
mod_gzip_item_exclude rspheader ^Content-Encoding:.*gzip.*
</IfModule>
# Leverage Browser Caching
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType image/jpg "access 1 year"
ExpiresByType image/jpeg "access 1 year"
ExpiresByType image/gif "access 1 year"
ExpiresByType image/png "access 1 year"
ExpiresByType text/css "access 1 month"
ExpiresByType text/html "access 1 month"
ExpiresByType application/pdf "access 1 month"
ExpiresByType text/x-javascript "access 1 month"
ExpiresByType application/x-shockwave-flash "access 1 month"
ExpiresByType image/x-icon "access 1 year"
ExpiresDefault "access 1 month"
</IfModule>
<IfModule mod_headers.c>
<filesmatch "\.(ico|flv|jpg|jpeg|png|gif|css|swf)__aSyNcId_<_AsyPazbF__quot;>
Header set Cache-Control "max-age=2678400, public"
</filesmatch>
<filesmatch "\.(html|htm)__aSyNcId_<_AsyPazbF__quot;>
Header set Cache-Control "max-age=7200, private, must-revalidate"
</filesmatch>
<filesmatch "\.(pdf)__aSyNcId_<_AsyPazbF__quot;>
Header set Cache-Control "max-age=86400, public"
</filesmatch>
<filesmatch "\.(js)__aSyNcId_<_AsyPazbF__quot;>
Header set Cache-Control "max-age=2678400, private"
</filesmatch>
</IfModule>
Now if you refresh your app in browser the app will work fine