Best practice: How to track outbound links? - web

How do you track outbound links for your web site, since the request is logged on the destination server, not yours?

You can add a quick JQuery script to the page that will track external links and can either redirect them to a file on your server that will track the link and then forward to it, or add an ajax request that will submit on click for external links, and track them that way.
See:
http://www.prodevtips.com/2008/08/19/tracking-clicks-with-jquery-and-google-analytics/
https://web.archive.org/web/20090214024330/http://www.justskins.com/development/how-to-track-clicks-on-outgoing-links/132

Method #1: target="_blank", onclick and Google Analytics Events
Format your outgoing links with the following attributes:
outgoing
Define a javascript tracking function (requires google analytics to be loaded already):
function trackOutgoing(el) {
ga('send', 'event', {eventCategory: 'outbound',
eventAction: 'send',
eventLabel: el.getAttribute('href'),
eventValue: 1});
};
Pros:
Does NOT interfere with normal link behavior
Does NOT require redirecting to another url
Cons:
The onclick is not guaranteed to execute (user or browser could terminate the main window)
Method #2: Redirecting with Javascript and Google Analytics Callbacks
Format your outgoing links with the following attributes:
outgoing
Define a javascript tracking function (requires google analytics to be loaded already):
function trackOutgoingAndRedirect(el) {
var url = el.getAttribute('href');
ga('send', 'event', {eventCategory: 'outbound',
eventAction: 'send',
eventLabel: url,
eventValue: 1,
hitCallback: function() { document.location = url; }});
}
Pros:
Does not require target="_blank"
Higher chance of your event being registered with Google Analytics (compared to Method #1)
Cons:
Overrides the default behavior of links with return false;
Cannot open outgoing links in a new window
Method #3: Using a Redirect URL
Format your outgoing links with the following attributes:
outgoing
On your site you will need to implement a redirect script which is beyond the scope of this answer.
Your redirect script would most likely track the outgoing link and then redirect to the provided url.
Pros:
No Javascript required
Does NOT require Google Analytics
Does NOT interfere with the normal link behavior
Cons:
Harder to trigger Google Analytics Events
Links do not link to their original URL. Which may have negative SEO implications.

Add an onclick or onmousedown handler to the anchor tag. You can see many sites doing this, such as Google.

I don't like the redirect as described by Eric Tuttleman, as you unfortunately lose the 'search engine friendliness' of the link.
I handle this on a site I own by adding an onClick to my outgoing links, which fires a function which sends the link URL and a timestamp to my database. I then wrote a backend which retrieves the data, and lets me view it by such categories as 'Most clicked / 24h', 'Most clicked / 1w' etc.
I hope this helps.

On one system I've worked on, we ended up storing redirects in a database table and creating a redirect page that takes an id as an input. On our content pages, we link to the redirect page with an unique id from this table. Once the redirect page looks up the url via the id from the table, it then sends the client a redirect response, sending them to the ending page.
This does give us logging of external links, and as an added bonus, it makes mass changes to external urls a bit easier in some cases.

Some newer options that work without any hacks as explained in https://css-tricks.com/send-an-http-request-on-page-exit/ are Fetch with the keepalive-flag or navigator.sendBeacon.
keepalive is not yet (Aug. 2022) supported by Firefox (Can I Use), but navigator.sendBeacon works in all modern browsers (Can I Use).
// normal fetch, not guaranteed to work
someLink.addEventListener('click', function(event){
fetch('http://www.testing.local/?origin=classic-fetch');
});
// fetch + keep alive (not working in Firefox as of 103, Aug. 2022)
someLink.addEventListener('click', function(event){
fetch('http://www.testing.local/?origin=fetch-keep-alive', {
keepalive: true
});
});
// navigator.sendBeacon (all modern browsers)
someLink.addEventListener('click', function(event){
navigator.sendBeacon('http://www.testing.local/?origin=beacon');
});

Related

Can I use Chrome declarativeNetRequest to completely replace Chrome webRequest?

I found chrome.declarativeNetRequest only supports static rules, What I want is to call some custom methods before actions like redirect/request. I haven't found a solution so far. I'm not sure if I can still do this under the Manifest V3.
There are two usecases for my extension.
Before the redirect, I need to execute custom method.
chrome.webRequest.onBeforeRequest.addListener(
function(requestDetails) {
//
// I can get id from requestDetails.url,
// then do some custom business logic.
//
custom_function(requestDetails.url);
return {redirectUrl:"javascript:"};
},
{urls: [ "url_pattern?id=*" ]},
["blocking"]
);
Before some request, I want add/modify requestHeaders according to the user's browser.
chrome.webRequest.onBeforeSendHeaders.addListener(
function (details) {
details.requestHeaders.push({
"name": "User-Agent",
"value": navigator.userAgent + "version_1.0.0"
});
return {requestHeaders: details.requestHeaders};
},
{
urls: ["*://url_pattern"],
types: ["xmlhttprequest"]
},
["blocking", "requestHeaders"]
);
#wOxxOm Thank you very much for your patient answer !
I prefer to spinner.html. But I have another problem.
I can't set the regexSubstitution to the extension address,
I can use the extensionPath, but the corresponding capture groups doesn't work here.
"regexFilter": "google.com*"
The following are all incorrect:
can't use the corresponding capture groups.
"extensionPath": "/spinner.html?url=\\0"
can't use the extension's address.
"regexSubstitution": "spinner.html?url=\\0"
Is my configuration incorrect?
Adding/deleting headers can only accept static values and it's shown in the official example.
Conditionally adding/deleting/modifying headers based on response headers is tracked in https://crbug.com/1141166.
Nontrivial transformations that exceed the functionality of the actions listed in the documentation naturally cannot be re-implemented.
When https://crbug.com/1262147 is fixed we will be able to define a declarativeNetRequest rule to redirect to a page inside your extension via regexSubstitution or extensionPath and append the original URL as a parameter. This page will act as an interstitial, it will display some kind of UI or a simple progress spinner, process the URL parameters, and redirect the current tab to another URL.
In many cases this approach would introduce flicker and unnecessary visual fuss while the interstitial is displayed shortly, thus frustrating users who will likely abandon using such extensions altogether. Chromium team members who work on extensions seem to think this obscene workaround is acceptable so it's likely they'll roll with it, see also https://crbug.com/1013582.
Use the observational webRequest (without 'blocking' parameter) and chrome.tabs.update to redirect the tab. The downside is that the original request will be sent to the remote server. And this approach obviously won't work for iframes, to redirect those you'll have to inject/declare a content script, to which your webRequest listener would send a message with a frameId parameter.
Keep a visible tab with an html page from your extension, and use the blocking chrome.webRequest inside its scripts. It's a terrible UX, of course, even though endorsed by the Chromium's extensions team, with many extensions using this kludge the user's browsers will have to keep a lot of such tabs open.
P.S. The blocking webRequest will be still available for force-installed extensions via policies, but it's not something most users would be willing to use.

Docusign return parameters in embedded signing by breaking out iframe

How to pass parameters(envelope,PF and r ID) within iframe while returning URL in embedded docusigning? If I enter the POWERFORM link on browser I'm returning URL with the parameters (envelope,PF and r ID) but if I run code within iframe I'm unable to get the parameters. Please do assist me about this issue.
You are opening Powerform inside an IFrame, so the scope of the opened URL is inside the IFrame only and DocuSign cannot do anything to redirect the browser to come out of the IFrame. You have write a code on your end to capture the redirect URL and break the flow out of IFrame, you can find a similar query here. Normally DocuSign does not recommend using IFrame for Signing, also to capture the data like envelopeId, r Id etc, it is better to configure DocuSign Connect with a listener on your side. Using url redirect is a fragile solution as user might close the browser (or browser hangs/network issue) and you might lose the data. Whereas with DS Connect, DocuSign will publish the event to your listener and you will be able to capture all the required data in your listener.
<script>
function myFunction()
{
var x = document.getElementById("form1").action;
document.getElementById("demo").innerHTML = x;
}
</script>
This thing works for displaying the parameters.
window.parent.window.location.href = 'Parent URL' works to break out of an iframe and load the parent page.

Google analytics stores username and password as a part of url

Issue Context:
I am using meteor js for a mobile app.
I have hooked it up with google analytics calls and basically I am using two type of calls:
Screen views
Events
Screen views are just fine, but I'm facing an issue with the events.
When I go to Behavior -> Events -> Screens, in the google analytics dashboard, I can see the URL of every page that has triggered an event under the Screen Name column. My problem is that the page URLs for my login page look something like this:
meteor.local/login?username=*******&password=+++++++&rememberMe=on
Where ******* is an actual username and +++++++ is the corresponding password!
Reason:
Since I have to share this analytics account with multiple people, I do not want this information to be available over here.
Clues:
CLUE 1:
I used to do GET http calls, but I have changed them all to POST and it still has not fixed the issue as I expected it not to pass plain parameters through URL anymore.
CLUE 2:
I've noticed that the default google analytics js framework is working with http and not https. I was wondering if it is calling the analytics server with a GET as well. If so, is there anyway to change that?
CLUE 3:
Here is how I am initiating the GA instance:
(function (i, s, o, g, r, a, m) {
i['GoogleAnalyticsObject'] = r;
i[r] = i[r] || function () {
(i[r].q = i[r].q || []).push(arguments)
}, i[r].l = 1 * new Date();
a = s.createElement(o),
m = s.getElementsByTagName(o)[0];
a.async = 1;
a.src = g;
m.parentNode.insertBefore(a, m)
})(window, document, 'script', '//www.google-analytics.com/analytics.js', 'ga');
ga('create', googleKey, 'auto');
CLUE 4:
I have also noticed that these URLs are getting captured very occasionally. E.g. in the pas 12,500 unique events (about 30,000 total events) it has captured just 9 URLs with the username and password. The remaining 12,491 events have
meteor.local/login
OR
meteor.local/--
OR
localhost/--
as the Screen Name.
CLUE 5:
I have also put 4 "search and replace" global filters on the analytics account to search for this string
meteor.local/.*
and replace it with this one
meteor.local/concealedURI
This does not seem to be working either.
I have added this filter on 4 different fields (Since I still really don't know where the URLs are coming from):
Host Name
Page Title
Referral
Request URI
CLUE 6:
This is how I am calling the GA instance to send the event:
ga('send', 'event', 'button', 'click', eventName);
Okay. So, I had to run a lot of experiments and try out different things to solve this issue.
After trying all the things that I have described in the question, I finally found a way to address this problem.
The main cause of this problem was that I was using a google analytics account set to track an App, to capture the data from an app that was built with meteor js (which basically utilizes cordova).
Using meteor means that my app's screens are actually web pages rendered as a mobile app. It seems like meteor uses URLs to navigate through these screens.
On the other hand, google analytics looks at (and captures) the screen name of an app's page, when an event is triggered from that page. In native apps this screen name will be something similar to "About us", "Contact Us", "Home", etc.
Now since a meteor app is not the same, the screen name returned by meteor is actually the URL of the page that has triggered the event.
This does not have anything to do with the http calls (Whether or not they are GET or POST), because it is the local URL used by meteor for navigating that is being passed down to google analytics and not any http calls.
Solutions
1.
If I had the google analytics account set as a web page tracker, I could have access to "Exclude URL Query Parameters" field and I could potentially exclude username and password as was suggested by #Mike and #PhilipPryde in the comments.
However, I needed to use google analytics set as an app tracker. So, this did not work for me.
Failed
2.
I did put a filter on the whole view in the google analytics and searched for meteor.local/.* and replaced that with hiddenURL. The filters on
Host Name
Page Title
Referral
Request URI
did not work.
But when I put the same filter on
Screen Label
field, it worked.
However, this only looked at the screen names returned by screen view hits and not the event. Thus, this did not actually solve my issue either.
Failed
Finally, I had to do this:
There is a method call on GA instance that lets you set different options up. I ended up using this:
ga('set', 'screenName', 'hiddenURL');
This changed the screen name to "hiddenURL". So, I used this before every event and it worked for me.
My code for sending events to google analytics looked like this:
ga('set', 'screenName', 'hiddenURL');
ga('send', 'event', 'button', 'click', eventName);
PS:
This changes the screen name that was showing up in real-time reports of google analytics to "hiddenURL", whenever someone triggered an event. But, it changes back to a screen name as soon as they go to another page. So, it would not also mess with any of your screen view data either, since it is not being captured as a screen view.
Of course that is because, I pass the screen name to my GA instance every time I send a screen view. So it looks like this:
sendScreenViewToGA = function (screenName) {
ga('send', 'screenview', {
'appName': 'Something',
'screenName': screenName,
'appVersion': x.x
});
}
If I had used the screen name, that is being set on the environment tight now, I would have ended up with all my screen names in analytics set to "hiddenURL".
I really hope this post will help others with same issues and save them some time.

Is it possible to rewrite url (with extra parameters) with a Chrome extension

I am trying to append few extra parameters to the url that user typed (before the page gets loaded). Is it possible to do?
For example, if user types www.google.com, I would like to append ?q=query to url (final: www.google.com?q=query.
Thanks
The webRequest API might be what you need. This code goes in your background page:
chrome.webRequest.onBeforeRequest.addListener(
function(details) {
if( details.url == "http://www.google.com/" )
return {redirectUrl: "http://www.google.com/?q=defaultquery" };
},
{urls: ["http://www.google.com/*"]},
["blocking"]);
This is an extremely specific rule that redirects visits to http://www.google.com/ with http://www.google.com/?q=defaultquery, but I think you can see how to expand it to include more functionality.
Note that this will reroute all attempts to reach http://www.google.com/, including Ajax requests and iframes.
Per the documentation, you will need to add the webRequest and webRequestBlocking permissions, along with host permissions for every host you plan to intercept:
"permissions": [
"webRequest",
"webRequestBlocking",
"*://*.google.com/",
...
],
This is an old question still I am answering it for future readers.
Modification of query parameters is a little tricky because you can endup in an infinite loop and Chrome/Firefox may detect it and process whatever is the current state of the request URL.
I have faced this situation in my chrome extension Requestly where Users used Replace Rule and replaced www.google.com with www.google.com?q=query or did something similar.
The problem with this approach is browsers intercept the request URL after adding query parameter so the parameter will be added multiple times and corrupt the URL. So you have to ensure either of the following:-
Do not intercept a request once it has been redirected.
Check if the parameter already exists, then do not redirect it.
As correctly pointed out by #apsillers in his answer, you have to use webRequest API to perform any modifications to the URL. Please have a look at his answer
and write your code accordingly.
Just in case, you are looking for an already available solution, consider trying Requestly's Query Parameter Rule. Here is a screenshot of how it looks like:-
For Firefox, you can download Requestly from its home page.

How to access developer tools (network log) from my extension

I'm writing an extension in Google chrome and Opera
I need to watch for specific URLs loaded, when i type one url in the address bar.
example:
if i goto www.google.com, it will load couple of other urls / images, etc.. those will be shown in Developer tools->Network.
Is there anyway, i can access that log from my extension background page?
Please let me know
Not really, but you can listen for load events that will fire on the images inside the page:
document.addEventListener('load', function(e){ /* e.target is a newly loaded image */ }, true)
Whether this solves your problem depends on what your use case is exactly..

Resources