I just finished building a PWA using Ionic. Now i am stuck in making PWA SEO enabled.
Since in ionic there is #(Hash location strategy) enabled in ionic and this results in # in all of the urls which doesn't help at all in SEO.
So to remove # I tried setting Path location strategy {locationStrategy: 'path'} in AppModule. This removes the # from the url. But when I try to refresh it or go directly to a url, it returns 'Cannot GET /url'
Few things I read for this problem is to rewrite to index.html in every request made Or try something as follows in node express.
app.get('*', (req, res, next) => {
if ((req.url.indexOf('#') > -1) ||
((req.url.lastIndexOf('.') === -1) ||
(req.url.indexOf('/', req.url.lastIndexOf('.')) > -1))) {
req.url = `/#${req.url}`;
}
next();
});
Both the above will not solve my problem because if I want to access a url directly i.e.. localhost/a/b . The above solutions will rewrite to localhost/home(Base URL) which I don't want.
Got it working myself.
So here is the list of things which I did.
Add the following code to each of the page for which you want to add description, keywords
ionViewWillEnter(){
try{
document.querySelector("meta[name='description']").remove();
} catch (e){
}
try{
document.querySelector("meta[name='keywords']").remove();
} catch (e){
}
var description = document.createElement('meta');
description.name = "description";
description.content = "I am a description";
document.getElementsByTagName('head')[0].appendChild(description);
var keywords = document.createElement('meta');
keywords.name = "keywords";
keywords.content = "Code, Learn, Respect";
document.getElementsByTagName('head')[0].appendChild(keywords);
}
Now we need to remove # in path of every url because SEO rejects the urls with # in them. In App Module , add {locationStrategy: 'path'} to your App Module as follows :
IonicModule.forRoot(MyApp, {
locationStrategy: 'path'
})
Now # is removed from the url. But when you refresh or directly access the url, this wont work because this is expected behaviour for any SPA Because when you refresh the page , server tries to find the page linked to the URL.
For eg: if you hit localhost/abc , then server tries to find abc/index.html which actually doesn't exist.So to resolve this , you have wrote configurations on my server i.e to point every request to index.html . I am using node express server to deploy your pwa. Use the following code to route every request to index.html -
var express = require('express');
var path = require('path')
var app = express();
app.use(express.static(path.resolve(__dirname, "www")));
app.use('/*', function(req, res){
res.sendFile(__dirname+ '/www' + '/index.html');
});
app.set('port', process.env.PORT || 3000);
app.listen(app.get('port'), function() {
console.log("listening to Port", app.get("port"));
});
- Also you need to put base href in index.html as '/'.
IONIC with SSR (Server Side Rendering) integrated with Firebase Hosting + Cloud Functions + Prerender.io
First of all, sorry for my poor English.
After searching through Deep Web (joking), I found the solution. And the coolest solution was that I was able to integrate my Pioneer Ionic application with Firebase Hosting using Cloud Functions.
After reading the following topic:
https://github.com/firebase/firebase-tools/issues/33
The @TheRoccoB user explains how to host the static Web application in Firebase Hosting and redirect traffic from a URL to Cloud Functions.
What I did was map the routes that I have to index as:
{
"source": "/ shop / **",
"function": "ssr"
},
{
"source": "/ * / promotions / **",
"function": "ssr"
}
Since "ssr" is the name of my function in Cloud Functions. So I used the library https://github.com/prerender/prerender-node to check if the request is from a google crowler, in case I redirect the request to a https://github.com/prerender/prerender server.
const prerender = express ();
prerender.use (cors);
prerender.use (
require ('prerender-node')
// .set ('prerenderServiceUrl', 'http: // localhost: 3000')
.set ('prerenderToken', '** TOKEN **')
);
prerender.use (require ('prerender-node'). set ('beforeRender', function (req, done) {
// do you need to do?
console.log ('Rendering URL:', req.path);
done ();
}));
prerender.use (require ('prerender-node') set ('afterRender', function (err, req, prerender_res) {
// do you need to do?
console.log (req.path + 'rendering completed!');
console.log ('Errors:', err);
}));
prerender.get ('*', (req, res) => {
console.log ('Calling function for URL:', req.path);
res.set ('Cache-Control', 'public, max-age = 300, s-maxage = 600');
res.status (200) .send (fs.readFileSync ('./ www / index.html'). toString ());
});
exports.ssr = functions.https.onRequest (prerender);