Is there a way to make the webbrowser submit additional HTTP header if the user clicks on a link?
Background: In our environment every http-request has a unique ID on the server side. See https://serverfault.com/questions/797609/apache-x-request-id-like-in-heroku
If your web application receives a http-request, I would like to know which page was the page before. The http referrer is not enough, since the user could use several tabs in his browser.
I would like to avoid to put the ugly request-id into every GET request which gets send from the browser to the server. Up to now our URLs are nice.
My prefered solution would be some JavaScript magic which adds the request-id of the current page into the next http request.
Steps in detail:
My goal is to track the relations "123 --> 456". Above solution is just a strategy to get to the goal. Other strategies are welcome.
We use the web framework django. But AFAIK this does matter in this context.
the user could use several tabs in his browser
I elaborate what that means for a matching solution. The sequence of requests which come from one user does not solve the issue.
One use with several tabs:
I want to know see two sequences:
A -> C -> D
And
B -> E
Select the web site where you want to add the custom HTTP response header. In the web site pane, double-click HTTP Response Headers in the IIS section. In the actions pane, select Add. In the Name box, type the custom HTTP header name.
You can't set HTTP headers as part of URL, it's not the way it works. At best you may be able to use a browser plugin to add/set arbitrary HTTP headers, but that will depend on the browser you are using. And will be a one off test/development option.
In the Home pane, double-click HTTP Response Headers. In the HTTP Response Headers pane, click Add... in the Actions pane. In the Add Custom HTTP Response Header dialog box, set the name and value for your custom header, and then click OK.
The only modern 'sane' option here is to use a ServiceWorker.
A ServiceWorker can intercept HTTP requests for a domain you control and decorate it with more headers.
A ServiceWorker works 'outside' of a browser tab, and if multiple tabs are open with the same website, the same serviceworker will be used for all of them.
A full tutorial on how to accomplish that is definitely too much for this answer box, but intercepting and doing stuff with HTTP requests is a big use-case, so off-site sources will usually have this as an example.
I would say that this is kind of a bad idea. If you think you need this, maybe you can handle this in a different way. A common way to do this might be using cookies instead.
We can modify request headers using:
It is not possible (using javascript) to change the headers sent by browser in a request like <a href=""></a>
because at least now, the http content negotiation is a browser's inner capability (except in part using XMLHttpRequest in same or allowed origins).
Then, in my opinion, as @Evert said you have two practical ways (a third in fact) to achieve your goal, performing a server proxy or using cookies. Here you have a very simple way using window.localStorage:
LocalStorage example
if (!localStorage.getItem("ids")) {//<-- the place in which we store the behavior
localStorage.setItem("ids", 'somevalue')
} else {
var ids = JSON.parse(localStorage.getItem("ids"));
ids.ids.push(id);//<-- we add some value
localStorage.setItem("ids", JSON.stringify(ids));
}
Full example here: https://jsfiddle.net/hy4rzob9/ press run several times and you'll see that we store each visit, of course, in your implementation you have to replace the random number for a unique identifier of each page.
LocalStorage example with several tabs
Taking into account the update, we could store the history using also document.referrer
with localStorage
with something like this:
var session = Math.random();
if(!localStorage.getItem("routes")){//<-- first time
var routes = {};
routes[session] = [document.location.href];
localStorage.setItem("routes", JSON.stringify(routes))
}else{
var routes = JSON.parse(localStorage.getItem("routes"));
if(!document.referrer){
routes[session] = [document.location.href];//<-- new root
}else{
for(let ses in routes){
if(routes[ses].includes(document.referrer)){
routes[ses].push(document.location.href);
}
}
}
localStorage.setItem("routes", JSON.stringify(routes))
}
var r = JSON.parse(localStorage.getItem("routes"));
console.log(r);
Full example here https://codesandbox.io/s/qk99o4vy7q, to emulate your example open this https://qk99o4vy7q.codesandbox.io/a.html (represents A) and open in a new tab https://qk99o4vy7q.codesandbox.io/b.html (represents B), navigate in both tabs and see the console. This example won't work if we share some referrer, because we can't differentiate between referrers if we attach nothing in the URL. A -> C -> D and B -> E will work, but A -> C -> D and B -> E -> A won't.
Ping example
There is other way, that is easy but has a limitation in browser compatibility, that is using ping
attribute of <a>
like this:
<a href="https://www.google.com/" ping="trackPing.py">Link to track</a>
ping Contains a space-separated list of URLs to which, when the hyperlink is followed, POST requests with the body PING will be sent by the browser (in the background). Typically used for tracking.
Open the console -> network, delete all, run the snippet and click in the link, if your browser supports it, you will see that the browser send a POST request to trackPing.py
(I guess doesn't exist in SO), that post is void but you could track the environmental variables such as request.environ['REMOTE_ADDR']
or something.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With